summaryrefslogtreecommitdiff
path: root/src/passes/OptimizeInstructions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/passes/OptimizeInstructions.cpp')
-rw-r--r--src/passes/OptimizeInstructions.cpp720
1 files changed, 368 insertions, 352 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 9cc16004d..1c2445d5d 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -28,6 +28,7 @@
#include <ir/literal-utils.h>
#include <ir/load-utils.h>
#include <ir/manipulation.h>
+#include <ir/match.h>
#include <ir/properties.h>
#include <ir/utils.h>
#include <pass.h>
@@ -132,6 +133,19 @@ struct LocalScanner : PostWalker<LocalScanner> {
}
};
+// Create a custom matcher for checking side effects
+template<class Opt> struct PureMatcherKind {};
+template<class Opt>
+struct Match::Internal::KindTypeRegistry<PureMatcherKind<Opt>> {
+ using matched_t = Expression*;
+ using data_t = Opt*;
+};
+template<class Opt> struct Match::Internal::MatchSelf<PureMatcherKind<Opt>> {
+ bool operator()(Expression* curr, Opt* opt) {
+ return !opt->effects(curr).hasSideEffects();
+ }
+};
+
// Main pass class
struct OptimizeInstructions
: public WalkerPass<
@@ -189,6 +203,20 @@ struct OptimizeInstructions
}
}
+ EffectAnalyzer effects(Expression* expr) {
+ return EffectAnalyzer(getPassOptions(), getModule()->features, expr);
+ }
+
+ decltype(auto) pure(Expression** binder) {
+ using namespace Match::Internal;
+ return Matcher<PureMatcherKind<OptimizeInstructions>>(binder, this);
+ }
+
+ bool canReorder(Expression* a, Expression* b) {
+ return EffectAnalyzer::canReorder(
+ getPassOptions(), getModule()->features, a, b);
+ }
+
// Optimizations that don't yet fit in the pattern DSL, but could be
// eventually maybe
Expression* handOptimize(Expression* curr) {
@@ -205,6 +233,104 @@ struct OptimizeInstructions
if (isSymmetric(binary)) {
canonicalize(binary);
}
+ }
+
+ {
+ // TODO: It is an ongoing project to port more transformations to the
+ // match API. Once most of the transformations have been ported, the
+ // `using namespace Match` can be hoisted to function scope and this extra
+ // block scope can be removed.
+ using namespace Match;
+ Builder builder(*getModule());
+ {
+ // X == 0 => eqz X
+ Expression* x;
+ if (matches(curr, binary(EqInt32, any(&x), i32(0)))) {
+ return Builder(*getModule()).makeUnary(EqZInt32, x);
+ }
+ }
+ {
+ // try to get rid of (0 - ..), that is, a zero only used to negate an
+ // int. an add of a subtract can be flipped in order to remove it:
+ // (i32.add
+ // (i32.sub
+ // (i32.const 0)
+ // X
+ // )
+ // Y
+ // )
+ // =>
+ // (i32.sub
+ // Y
+ // X
+ // )
+ // Note that this reorders X and Y, so we need to be careful about that.
+ Expression *x, *y;
+ Binary* sub;
+ if (matches(curr,
+ binary(AddInt32,
+ binary(&sub, SubInt32, i32(0), any(&x)),
+ any(&y))) &&
+ canReorder(x, y)) {
+ sub->left = y;
+ sub->right = x;
+ return sub;
+ }
+ }
+ {
+ // The flip case is even easier, as no reordering occurs:
+ // (i32.add
+ // Y
+ // (i32.sub
+ // (i32.const 0)
+ // X
+ // )
+ // )
+ // =>
+ // (i32.sub
+ // Y
+ // X
+ // )
+ Expression* y;
+ Binary* sub;
+ if (matches(curr,
+ binary(AddInt32,
+ any(&y),
+ binary(&sub, SubInt32, i32(0), any())))) {
+ sub->left = y;
+ return sub;
+ }
+ }
+ {
+ // try de-morgan's AND law,
+ // (eqz X) and (eqz Y) === eqz (X or Y)
+ // Note that the OR and XOR laws do not work here, as these
+ // are not booleans (we could check if they are, but a boolean
+ // would already optimize with the eqz anyhow, unless propagating).
+ // But for AND, the left is true iff X and Y are each all zero bits,
+ // and the right is true if the union of their bits is zero; same.
+ Unary* un;
+ Binary* bin;
+ Expression *x, *y;
+ if (matches(curr,
+ binary(&bin,
+ AndInt32,
+ unary(&un, EqZInt32, any(&x)),
+ unary(EqZInt32, any(&y))))) {
+ bin->op = OrInt32;
+ bin->left = x;
+ bin->right = y;
+ un->value = bin;
+ return un;
+ }
+ }
+ }
+
+ if (auto* select = curr->dynCast<Select>()) {
+ return optimizeSelect(select);
+ }
+
+ if (auto* binary = curr->dynCast<Binary>()) {
if (auto* ext = Properties::getAlmostSignExt(binary)) {
Index extraShifts;
auto bits = Properties::getAlmostSignExtBits(binary, extraShifts);
@@ -313,57 +439,6 @@ struct OptimizeInstructions
// note that both left and right may be consts, but then we let
// precompute compute the constant result
} else if (binary->op == AddInt32) {
- // try to get rid of (0 - ..), that is, a zero only used to negate an
- // int. an add of a subtract can be flipped in order to remove it:
- // (i32.add
- // (i32.sub
- // (i32.const 0)
- // X
- // )
- // Y
- // )
- // =>
- // (i32.sub
- // Y
- // X
- // )
- // Note that this reorders X and Y, so we need to be careful about that.
- if (auto* sub = binary->left->dynCast<Binary>()) {
- if (sub->op == SubInt32) {
- if (auto* subZero = sub->left->dynCast<Const>()) {
- if (subZero->value.geti32() == 0) {
- if (EffectAnalyzer::canReorder(
- getPassOptions(), features, sub->right, binary->right)) {
- sub->left = binary->right;
- return sub;
- }
- }
- }
- }
- }
- // The flip case is even easier, as no reordering occurs:
- // (i32.add
- // Y
- // (i32.sub
- // (i32.const 0)
- // X
- // )
- // )
- // =>
- // (i32.sub
- // Y
- // X
- // )
- if (auto* sub = binary->right->dynCast<Binary>()) {
- if (sub->op == SubInt32) {
- if (auto* subZero = sub->left->dynCast<Const>()) {
- if (subZero->value.geti32() == 0) {
- sub->left = binary->left;
- return sub;
- }
- }
- }
- }
if (auto* ret = optimizeAddedConstants(binary)) {
return ret;
}
@@ -463,30 +538,6 @@ struct OptimizeInstructions
}
}
// bitwise operations
- if (binary->op == AndInt32) {
- // try de-morgan's AND law,
- // (eqz X) and (eqz Y) === eqz (X or Y)
- // Note that the OR and XOR laws do not work here, as these
- // are not booleans (we could check if they are, but a boolean
- // would already optimize with the eqz anyhow, unless propagating).
- // But for AND, the left is true iff X and Y are each all zero bits,
- // and the right is true if the union of their bits is zero; same.
- if (auto* left = binary->left->dynCast<Unary>()) {
- if (left->op == EqZInt32) {
- if (auto* right = binary->right->dynCast<Unary>()) {
- if (right->op == EqZInt32) {
- // reuse one unary, drop the other
- auto* leftValue = left->value;
- left->value = binary;
- binary->left = leftValue;
- binary->right = right->value;
- binary->op = OrInt32;
- return left;
- }
- }
- }
- }
- }
// for and and or, we can potentially conditionalize
if (binary->op == AndInt32 || binary->op == OrInt32) {
if (auto* ret = conditionalizeExpensiveOnBitwise(binary)) {
@@ -507,8 +558,7 @@ struct OptimizeInstructions
}
// finally, try more expensive operations on the binary in
// the case that they have no side effects
- if (!EffectAnalyzer(getPassOptions(), features, binary->left)
- .hasSideEffects()) {
+ if (!effects(binary->left).hasSideEffects()) {
if (ExpressionAnalyzer::equal(binary->left, binary->right)) {
if (auto* ret = optimizeBinaryWithEqualEffectlessChildren(binary)) {
return ret;
@@ -563,9 +613,7 @@ struct OptimizeInstructions
// sides are identical, fold
// if we can replace the if with one arm, and no side effects in the
// condition, do that
- auto needCondition =
- EffectAnalyzer(getPassOptions(), features, iff->condition)
- .hasSideEffects();
+ auto needCondition = effects(iff->condition).hasSideEffects();
auto isSubType = Type::isSubType(iff->ifTrue->type, iff->type);
if (isSubType && !needCondition) {
return iff->ifTrue;
@@ -591,92 +639,6 @@ struct OptimizeInstructions
}
}
}
- } else if (auto* select = curr->dynCast<Select>()) {
- select->condition = optimizeBoolean(select->condition);
- if (auto* c = select->condition->dynCast<Const>()) {
- // constant condition, we can just pick the right side (barring side
- // effects)
- if (c->value.getInteger()) {
- if (!EffectAnalyzer(getPassOptions(), features, select->ifFalse)
- .hasSideEffects()) {
- return select->ifTrue;
- } else {
- // don't bother - we would need to reverse the order using a temp
- // local, which is bad
- }
- } else {
- if (!EffectAnalyzer(getPassOptions(), features, select->ifTrue)
- .hasSideEffects()) {
- return select->ifFalse;
- } else {
- Builder builder(*getModule());
- return builder.makeSequence(builder.makeDrop(select->ifTrue),
- select->ifFalse);
- }
- }
- }
- if (auto* constTrue = select->ifTrue->dynCast<Const>()) {
- if (auto* constFalse = select->ifFalse->dynCast<Const>()) {
- if (select->type == Type::i32 || select->type == Type::i64) {
- auto trueValue = constTrue->value.getInteger();
- auto falseValue = constFalse->value.getInteger();
- if ((trueValue == 1LL && falseValue == 0LL) ||
- (trueValue == 0LL && falseValue == 1LL)) {
- Builder builder(*getModule());
- Expression* condition = select->condition;
- if (trueValue == 0LL) {
- condition =
- optimizeBoolean(builder.makeUnary(EqZInt32, condition));
- }
- if (!Properties::emitsBoolean(condition)) {
- // expr ? 1 : 0 ==> !!expr
- condition = builder.makeUnary(
- EqZInt32, builder.makeUnary(EqZInt32, condition));
- }
- return select->type == Type::i64
- ? builder.makeUnary(ExtendUInt32, condition)
- : condition;
- }
- }
- }
- }
- if (auto* condition = select->condition->dynCast<Unary>()) {
- if (condition->op == EqZInt32) {
- // flip select to remove eqz, if we can reorder
- EffectAnalyzer ifTrue(getPassOptions(), features, select->ifTrue);
- EffectAnalyzer ifFalse(getPassOptions(), features, select->ifFalse);
- if (!ifTrue.invalidates(ifFalse)) {
- select->condition = condition->value;
- std::swap(select->ifTrue, select->ifFalse);
- }
- }
- }
- if (ExpressionAnalyzer::equal(select->ifTrue, select->ifFalse)) {
- // sides are identical, fold
- EffectAnalyzer value(getPassOptions(), features, select->ifTrue);
- if (value.hasSideEffects()) {
- // at best we don't need the condition, but need to execute the value
- // twice. a block is larger than a select by 2 bytes, and
- // we must drop one value, so 3, while we save the condition,
- // so it's not clear this is worth it, TODO
- } else {
- // value has no side effects
- EffectAnalyzer condition(
- getPassOptions(), features, select->condition);
- if (!condition.hasSideEffects()) {
- return select->ifTrue;
- } else {
- // the condition is last, so we need a new local, and it may be
- // a bad idea to use a block like we do for an if. Do it only if we
- // can reorder
- if (!condition.invalidates(value)) {
- Builder builder(*getModule());
- return builder.makeSequence(builder.makeDrop(select->condition),
- select->ifTrue);
- }
- }
- }
- }
} else if (auto* br = curr->dynCast<Break>()) {
if (br->condition) {
br->condition = optimizeBoolean(br->condition);
@@ -734,15 +696,12 @@ private:
// write more concise pattern matching code elsewhere.
void canonicalize(Binary* binary) {
assert(isSymmetric(binary));
- FeatureSet features = getModule()->features;
auto swap = [&]() {
- assert(EffectAnalyzer::canReorder(
- getPassOptions(), features, binary->left, binary->right));
+ assert(canReorder(binary->left, binary->right));
std::swap(binary->left, binary->right);
};
auto maybeSwap = [&]() {
- if (EffectAnalyzer::canReorder(
- getPassOptions(), features, binary->left, binary->right)) {
+ if (canReorder(binary->left, binary->right)) {
swap();
}
};
@@ -860,6 +819,89 @@ private:
return boolean;
}
+ Expression* optimizeSelect(Select* curr) {
+ using namespace Match;
+ Builder builder(*getModule());
+ curr->condition = optimizeBoolean(curr->condition);
+ {
+ // Constant condition, we can just pick the correct side (barring side
+ // effects)
+ Expression *ifTrue, *ifFalse;
+ if (matches(curr, select(pure(&ifTrue), any(&ifFalse), i32(0)))) {
+ return ifFalse;
+ }
+ if (matches(curr, select(any(&ifTrue), any(&ifFalse), i32(0)))) {
+ return builder.makeSequence(builder.makeDrop(ifTrue), ifFalse);
+ }
+ int32_t cond;
+ if (matches(curr, select(any(&ifTrue), pure(&ifFalse), i32(&cond)))) {
+ // The condition must be non-zero because a zero would have matched one
+ // of the previous patterns.
+ assert(cond != 0);
+ return ifTrue;
+ }
+ // Don't bother when `ifFalse` isn't pure - we would need to reverse the
+ // order using a temp local, which would be bad
+ }
+ {
+ // Flip select to remove eqz if we can reorder
+ Select* s;
+ Expression *ifTrue, *ifFalse, *c;
+ if (matches(
+ curr,
+ select(
+ &s, any(&ifTrue), any(&ifFalse), unary(EqZInt32, any(&c)))) &&
+ canReorder(ifTrue, ifFalse)) {
+ s->ifTrue = ifFalse;
+ s->ifFalse = ifTrue;
+ s->condition = c;
+ }
+ }
+ {
+ // Simplify selects between 0 and 1
+ Expression* c;
+ bool reversed = matches(curr, select(ival(0), ival(1), any(&c)));
+ if (reversed || matches(curr, select(ival(1), ival(0), any(&c)))) {
+ if (reversed) {
+ c = optimizeBoolean(builder.makeUnary(EqZInt32, c));
+ }
+ if (!Properties::emitsBoolean(c)) {
+ // cond ? 1 : 0 ==> !!cond
+ c = builder.makeUnary(EqZInt32, builder.makeUnary(EqZInt32, c));
+ }
+ return curr->type == Type::i64 ? builder.makeUnary(ExtendUInt32, c) : c;
+ }
+ }
+ {
+ // Sides are identical, fold
+ Expression *ifTrue, *ifFalse, *c;
+ if (matches(curr, select(any(&ifTrue), any(&ifFalse), any(&c))) &&
+ ExpressionAnalyzer::equal(ifTrue, ifFalse)) {
+ auto value = effects(ifTrue);
+ if (value.hasSideEffects()) {
+ // At best we don't need the condition, but need to execute the
+ // value twice. a block is larger than a select by 2 bytes, and we
+ // must drop one value, so 3, while we save the condition, so it's
+ // not clear this is worth it, TODO
+ } else {
+ // value has no side effects
+ auto condition = effects(c);
+ if (!condition.hasSideEffects()) {
+ return ifTrue;
+ } else {
+ // The condition is last, so we need a new local, and it may be a
+ // bad idea to use a block like we do for an if. Do it only if we
+ // can reorder
+ if (!condition.invalidates(value)) {
+ return builder.makeSequence(builder.makeDrop(c), ifTrue);
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+ }
+
// find added constants in an expression tree, including multiplied/shifted,
// and combine them note that we ignore division/shift-right, as rounding
// makes this nonlinear, so not a valid opt
@@ -1024,9 +1066,8 @@ private:
if (!Properties::emitsBoolean(left) || !Properties::emitsBoolean(right)) {
return nullptr;
}
- FeatureSet features = getModule()->features;
- auto leftEffects = EffectAnalyzer(getPassOptions(), features, left);
- auto rightEffects = EffectAnalyzer(getPassOptions(), features, right);
+ auto leftEffects = effects(left);
+ auto rightEffects = effects(right);
auto leftHasSideEffects = leftEffects.hasSideEffects();
auto rightHasSideEffects = rightEffects.hasSideEffects();
if (leftHasSideEffects && rightHasSideEffects) {
@@ -1072,16 +1113,13 @@ private:
// (x > y) | (x == y) ==> x >= y
Expression* combineOr(Binary* binary) {
assert(binary->op == OrInt32);
- FeatureSet features = getModule()->features;
if (auto* left = binary->left->dynCast<Binary>()) {
if (auto* right = binary->right->dynCast<Binary>()) {
if (left->op != right->op &&
ExpressionAnalyzer::equal(left->left, right->left) &&
ExpressionAnalyzer::equal(left->right, right->right) &&
- !EffectAnalyzer(getPassOptions(), features, left->left)
- .hasSideEffects() &&
- !EffectAnalyzer(getPassOptions(), features, left->right)
- .hasSideEffects()) {
+ !effects(left->left).hasSideEffects() &&
+ !effects(left->right).hasSideEffects()) {
switch (left->op) {
// (x > y) | (x == y) ==> x >= y
case EqInt32: {
@@ -1202,185 +1240,165 @@ private:
// optimize trivial math operations, given that the right side of a binary
// is a constant
- // TODO: templatize on type?
- Expression* optimizeWithConstantOnRight(Binary* binary) {
- FeatureSet features = getModule()->features;
- auto type = binary->right->type;
- auto* right = binary->right->cast<Const>();
- if (type.isInteger()) {
- auto constRight = right->value.getInteger();
- // operations on zero
- if (constRight == 0LL) {
- if (binary->op == Abstract::getBinary(type, Abstract::Shl) ||
- binary->op == Abstract::getBinary(type, Abstract::ShrU) ||
- binary->op == Abstract::getBinary(type, Abstract::ShrS) ||
- binary->op == Abstract::getBinary(type, Abstract::Or) ||
- binary->op == Abstract::getBinary(type, Abstract::Xor)) {
- return binary->left;
- } else if ((binary->op == Abstract::getBinary(type, Abstract::Mul) ||
- binary->op == Abstract::getBinary(type, Abstract::And)) &&
- !EffectAnalyzer(getPassOptions(), features, binary->left)
- .hasSideEffects()) {
- return binary->right;
- } else if (binary->op == Abstract::getBinary(type, Abstract::Eq)) {
- return Builder(*getModule())
- .makeUnary(Abstract::getUnary(type, Abstract::EqZ), binary->left);
- }
- }
- // operations on one
- if (constRight == 1LL) {
- // (signed)x % 1 ==> 0
- if (binary->op == Abstract::getBinary(type, Abstract::RemS) &&
- !EffectAnalyzer(getPassOptions(), features, binary->left)
- .hasSideEffects()) {
- right->value = Literal::makeSingleZero(type);
- return right;
- }
- // bool(x) | 1 ==> 1
- // bool(x) & 1 ==> bool(x)
- // bool(x) == 1 ==> bool(x)
- // bool(x) != 1 ==> !bool(x)
- if (Bits::getMaxBits(binary->left, this) == 1) {
- switch (binary->op) {
- case OrInt32:
- case OrInt64: {
- if (!EffectAnalyzer(getPassOptions(), features, binary->left)
- .hasSideEffects()) {
- // bool(x) | 1 ==> 1
- return binary->right;
- }
- break;
- }
- case AndInt32:
- case AndInt64:
- case EqInt32: {
- // bool(x) & 1 ==> bool(x)
- // bool(x) == 1 ==> bool(x)
- return binary->left;
- }
- case EqInt64: {
- // i64(bool(x)) == 1 ==> i32(bool(x))
- return Builder(*getModule()).makeUnary(WrapInt64, binary->left);
- }
- case NeInt32:
- case NeInt64: {
- // bool(x) != 1 ==> !bool(x)
- return Builder(*getModule())
- .makeUnary(
- Abstract::getUnary(binary->left->type, Abstract::EqZ),
- binary->left);
- }
- default: {
- }
- }
- }
- }
- // operations on all 1s
- if (constRight == -1LL) {
- if (binary->op == Abstract::getBinary(type, Abstract::And)) {
- // x & -1 ==> x
- return binary->left;
- } else if (binary->op == Abstract::getBinary(type, Abstract::Or) &&
- !EffectAnalyzer(getPassOptions(), features, binary->left)
- .hasSideEffects()) {
- // x | -1 ==> -1
- return binary->right;
- } else if (binary->op == Abstract::getBinary(type, Abstract::RemS) &&
- !EffectAnalyzer(getPassOptions(), features, binary->left)
- .hasSideEffects()) {
- // (signed)x % -1 ==> 0
- right->value = Literal::makeSingleZero(type);
- return right;
- } else if (binary->op == Abstract::getBinary(type, Abstract::GtU) &&
- !EffectAnalyzer(getPassOptions(), features, binary->left)
- .hasSideEffects()) {
- // (unsigned)x > -1 ==> 0
- right->value = Literal::makeSingleZero(Type::i32);
- right->type = Type::i32;
- return right;
- } else if (binary->op == Abstract::getBinary(type, Abstract::LtU)) {
- // (unsigned)x < -1 ==> x != -1
- // friendlier to JS emitting as we don't need to write an unsigned
- // -1 value which is large.
- binary->op = Abstract::getBinary(type, Abstract::Ne);
- return binary;
- } else if (binary->op == DivUInt32) {
- // (unsigned)x / -1 ==> x == -1
- binary->op = Abstract::getBinary(type, Abstract::Eq);
- return binary;
- } else if (binary->op == Abstract::getBinary(type, Abstract::Mul)) {
- // x * -1 ==> 0 - x
- binary->op = Abstract::getBinary(type, Abstract::Sub);
- right->value = Literal::makeSingleZero(type);
- std::swap(binary->left, binary->right);
- return binary;
- } else if (binary->op == Abstract::getBinary(type, Abstract::LeU) &&
- !EffectAnalyzer(getPassOptions(), features, binary->left)
- .hasSideEffects()) {
- // (unsigned)x <= -1 ==> 1
- right->value = Literal::makeFromInt32(1, Type::i32);
- right->type = Type::i32;
- return right;
- }
- }
- // wasm binary encoding uses signed LEBs, which slightly favor negative
+ Expression* optimizeWithConstantOnRight(Binary* curr) {
+ using namespace Match;
+ Builder builder(*getModule());
+ Expression* left;
+ auto* right = curr->right->cast<Const>();
+ auto type = curr->right->type;
+
+ // Operations on zero
+ if (matches(curr, binary(Abstract::Shl, any(&left), ival(0))) ||
+ matches(curr, binary(Abstract::ShrU, any(&left), ival(0))) ||
+ matches(curr, binary(Abstract::ShrS, any(&left), ival(0))) ||
+ matches(curr, binary(Abstract::Or, any(&left), ival(0))) ||
+ matches(curr, binary(Abstract::Xor, any(&left), ival(0)))) {
+ return left;
+ }
+ if (matches(curr, binary(Abstract::Mul, pure(&left), ival(0))) ||
+ matches(curr, binary(Abstract::And, pure(&left), ival(0)))) {
+ return right;
+ }
+ // x == 0 ==> eqz x
+ if ((matches(curr, binary(Abstract::Eq, any(&left), ival(0))))) {
+ return builder.makeUnary(EqZInt64, left);
+ }
+
+ // Operations on one
+ // (signed)x % 1 ==> 0
+ if (matches(curr, binary(Abstract::RemS, pure(&left), ival(1)))) {
+ right->value = Literal::makeSingleZero(type);
+ return right;
+ }
+ // bool(x) | 1 ==> 1
+ if (matches(curr, binary(Abstract::Or, pure(&left), ival(1))) &&
+ Bits::getMaxBits(left, this) == 1) {
+ return right;
+ }
+ // bool(x) & 1 ==> bool(x)
+ if (matches(curr, binary(Abstract::And, any(&left), ival(1))) &&
+ Bits::getMaxBits(left, this) == 1) {
+ return left;
+ }
+ // bool(x) == 1 ==> bool(x)
+ if (matches(curr, binary(EqInt32, any(&left), i32(1))) &&
+ Bits::getMaxBits(left, this) == 1) {
+ return left;
+ }
+ // i64(bool(x)) == 1 ==> i32(bool(x))
+ if (matches(curr, binary(EqInt64, any(&left), i64(1))) &&
+ Bits::getMaxBits(left, this) == 1) {
+ return builder.makeUnary(WrapInt64, left);
+ }
+ // bool(x) != 1 ==> !bool(x)
+ if (matches(curr, binary(Abstract::Ne, any(&left), ival(1))) &&
+ Bits::getMaxBits(curr->left, this) == 1) {
+ return builder.makeUnary(Abstract::getUnary(type, Abstract::EqZ), left);
+ }
+
+ // Operations on all 1s
+ // x & -1 ==> x
+ if (matches(curr, binary(Abstract::And, any(&left), ival(-1)))) {
+ return left;
+ }
+ // x | -1 ==> -1
+ if (matches(curr, binary(Abstract::Or, pure(&left), ival(-1)))) {
+ return right;
+ }
+ // (signed)x % -1 ==> 0
+ if (matches(curr, binary(Abstract::RemS, pure(&left), ival(-1)))) {
+ right->value = Literal::makeSingleZero(type);
+ return right;
+ }
+ // (unsigned)x > -1 ==> 0
+ if (matches(curr, binary(Abstract::GtU, pure(&left), ival(-1)))) {
+ right->value = Literal::makeSingleZero(Type::i32);
+ right->type = Type::i32;
+ return right;
+ }
+ // (unsigned)x < -1 ==> x != -1
+ // Friendlier to JS emitting as we don't need to write an unsigned -1 value
+ // which is large.
+ if (matches(curr, binary(Abstract::LtU, any(), ival(-1)))) {
+ curr->op = Abstract::getBinary(type, Abstract::Ne);
+ return curr;
+ }
+ // (unsigned)x / -1 ==> x == -1
+ // TODO: i64 as well if sign-extension is enabled
+ if (matches(curr, binary(DivUInt32, any(), ival(-1)))) {
+ curr->op = Abstract::getBinary(type, Abstract::Eq);
+ return curr;
+ }
+ // x * -1 ==> 0 - x
+ if (matches(curr, binary(Abstract::Mul, any(&left), ival(-1)))) {
+ right->value = Literal::makeSingleZero(type);
+ curr->op = Abstract::getBinary(type, Abstract::Sub);
+ curr->left = right;
+ curr->right = left;
+ return curr;
+ }
+ // (unsigned)x <= -1 ==> 1
+ if (matches(curr, binary(Abstract::LeU, pure(&left), ival(-1)))) {
+ right->value = Literal::makeFromInt32(1, Type::i32);
+ right->type = Type::i32;
+ return right;
+ }
+ {
+ // Wasm binary encoding uses signed LEBs, which slightly favor negative
// numbers: -64 is more efficient than +64 etc., as well as other powers
- // of two 7 bits etc. higher. we therefore prefer x - -64 over x + 64.
- // in theory we could just prefer negative numbers over positive, but
- // that can have bad effects on gzip compression (as it would mean more
- // subtractions than the more common additions).
- if (binary->op == Abstract::getBinary(type, Abstract::Add) ||
- binary->op == Abstract::getBinary(type, Abstract::Sub)) {
- auto value = constRight;
- if (value == 0x40 || value == 0x2000 || value == 0x100000 ||
- value == 0x8000000 || value == 0x400000000LL ||
- value == 0x20000000000LL || value == 0x1000000000000LL ||
- value == 0x80000000000000LL || value == 0x4000000000000000LL) {
- right->value = right->value.neg();
- if (binary->op == Abstract::getBinary(type, Abstract::Add)) {
- binary->op = Abstract::getBinary(type, Abstract::Sub);
- } else {
- binary->op = Abstract::getBinary(type, Abstract::Add);
- }
- return binary;
+ // of two 7 bits etc. higher. we therefore prefer x - -64 over x + 64. in
+ // theory we could just prefer negative numbers over positive, but that
+ // can have bad effects on gzip compression (as it would mean more
+ // subtractions than the more common additions). TODO: Simplify this by
+ // adding an ival matcher than can bind int64_t vars.
+ int64_t value;
+ if ((matches(curr, binary(Abstract::Add, any(), ival(&value))) ||
+ matches(curr, binary(Abstract::Sub, any(), ival(&value)))) &&
+ (value == 0x40 || value == 0x2000 || value == 0x100000 ||
+ value == 0x8000000 || value == 0x400000000LL ||
+ value == 0x20000000000LL || value == 0x1000000000000LL ||
+ value == 0x80000000000000LL || value == 0x4000000000000000LL)) {
+ right->value = right->value.neg();
+ if (matches(curr, binary(Abstract::Add, any(), constant()))) {
+ curr->op = Abstract::getBinary(type, Abstract::Sub);
+ } else {
+ curr->op = Abstract::getBinary(type, Abstract::Add);
}
+ return curr;
}
}
- if (type.isFloat()) {
- auto value = right->value.getFloat();
- if (value == 0.0) {
- if (binary->op == Abstract::getBinary(type, Abstract::Sub)) {
- if (std::signbit(value)) {
- // x - (-0.0) ==> x + 0.0
- binary->op = Abstract::getBinary(type, Abstract::Add);
- right->value = right->value.neg();
- return binary;
- } else {
- // x - 0.0 ==> x
- return binary->left;
- }
- } else if (binary->op == Abstract::getBinary(type, Abstract::Add)) {
- if (std::signbit(value)) {
- // x + (-0.0) ==> x
- return binary->left;
- }
+ {
+ double value;
+ if (matches(curr, binary(Abstract::Sub, any(), fval(&value))) &&
+ value == 0.0) {
+ // x - (-0.0) ==> x + 0.0
+ if (std::signbit(value)) {
+ curr->op = Abstract::getBinary(type, Abstract::Add);
+ right->value = right->value.neg();
+ return curr;
+ } else {
+ // x - 0.0 ==> x
+ return curr->left;
}
}
}
- if (type.isInteger() || type.isFloat()) {
- // note that this is correct even on floats with a NaN on the left,
- // as a NaN would skip the computation and just return the NaN,
- // and that is precisely what we do here. but, the same with -1
- // (change to a negation) would be incorrect for that reason.
- if (right->value == Literal::makeFromInt32(1, type)) {
- if (binary->op == Abstract::getBinary(type, Abstract::Mul) ||
- binary->op == Abstract::getBinary(type, Abstract::DivS) ||
- binary->op == Abstract::getBinary(type, Abstract::DivU)) {
- return binary->left;
- }
+ {
+ // x + (-0.0) ==> x
+ double value;
+ if (matches(curr, binary(Abstract::Add, any(), fval(&value))) &&
+ value == 0.0 && std::signbit(value)) {
+ return curr->left;
}
}
- // TODO: v128 not implemented yet
+ // Note that this is correct even on floats with a NaN on the left,
+ // as a NaN would skip the computation and just return the NaN,
+ // and that is precisely what we do here. but, the same with -1
+ // (change to a negation) would be incorrect for that reason.
+ if (matches(curr, binary(Abstract::Mul, any(&left), constant(1))) ||
+ matches(curr, binary(Abstract::DivS, any(&left), constant(1))) ||
+ matches(curr, binary(Abstract::DivU, any(&left), constant(1)))) {
+ return left;
+ }
return nullptr;
}
@@ -1397,9 +1415,7 @@ private:
if ((binary->op == Abstract::getBinary(type, Abstract::Shl) ||
binary->op == Abstract::getBinary(type, Abstract::ShrU) ||
binary->op == Abstract::getBinary(type, Abstract::ShrS)) &&
- !EffectAnalyzer(
- getPassOptions(), getModule()->features, binary->right)
- .hasSideEffects()) {
+ !effects(binary->right).hasSideEffects()) {
return binary->left;
}
}