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.cpp136
1 files changed, 121 insertions, 15 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 62c29b184..70e339241 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -24,6 +24,7 @@
#include <pass.h>
#include <wasm-s-parser.h>
#include <support/threads.h>
+#include <ir/abstract.h>
#include <ir/utils.h>
#include <ir/cost.h>
#include <ir/effects.h>
@@ -541,9 +542,11 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
}
}
}
- return optimizeAddedConstants(binary);
+ auto* ret = optimizeAddedConstants(binary);
+ if (ret) return ret;
} else if (binary->op == SubInt32) {
- return optimizeAddedConstants(binary);
+ auto* ret = optimizeAddedConstants(binary);
+ if (ret) return ret;
}
// a bunch of operations on a constant right side can be simplified
if (auto* right = binary->right->dynCast<Const>()) {
@@ -567,19 +570,9 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
}
}
}
- // some math operations have trivial results TODO: many more
- if (right->value == Literal(int32_t(0))) {
- if (binary->op == ShlInt32 || binary->op == ShrUInt32 || binary->op == ShrSInt32 || binary->op == OrInt32) {
- return binary->left;
- } else if ((binary->op == MulInt32 || binary->op == AndInt32) &&
- !EffectAnalyzer(getPassOptions(), binary->left).hasSideEffects()) {
- return binary->right;
- }
- } else if (right->value == Literal(int32_t(1))) {
- if (binary->op == MulInt32) {
- return binary->left;
- }
- }
+ // some math operations have trivial results
+ Expression* ret = optimizeWithConstantOnRight(binary);
+ if (ret) return ret;
// the square of some operations can be merged
if (auto* left = binary->left->dynCast<Binary>()) {
if (left->op == binary->op) {
@@ -645,6 +638,10 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
if (binary->op == AndInt32 || binary->op == OrInt32) {
return conditionalizeExpensiveOnBitwise(binary);
}
+ // relation/comparisons allow for math optimizations
+ if (binary->isRelational()) {
+ return optimizeRelational(binary);
+ }
} else if (auto* unary = curr->dynCast<Unary>()) {
// de-morgan's laws
if (unary->op == EqZInt32) {
@@ -1098,6 +1095,115 @@ private:
}
return false;
}
+
+ // optimize trivial math operations, given that the right side of a binary
+ // is a constant
+ // TODO: templatize on type?
+ Expression* optimizeWithConstantOnRight(Binary* binary) {
+ auto type = binary->right->type;
+ auto* right = binary->right->cast<Const>();
+ if (isIntegerType(type)) {
+ // operations on zero
+ if (right->value == LiteralUtils::makeLiteralFromInt32(0, type)) {
+ 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)) {
+ return binary->left;
+ } else if ((binary->op == Abstract::getBinary(type, Abstract::Mul) ||
+ binary->op == Abstract::getBinary(type, Abstract::And)) &&
+ !EffectAnalyzer(getPassOptions(), binary->left).hasSideEffects()) {
+ return binary->right;
+ }
+ }
+ // wasm binary encoding uses signed LEBs, which slightly favor negative
+ // numbers: -64 is more efficient than +64 etc. we therefore prefer
+ // x - -64 over x + 64
+ if (binary->op == Abstract::getBinary(type, Abstract::Add) ||
+ binary->op == Abstract::getBinary(type, Abstract::Sub)) {
+ auto value = right->value.getInteger();
+ 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);
+ }
+ }
+ }
+ }
+ // 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 == LiteralUtils::makeLiteralFromInt32(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;
+ }
+ }
+ return nullptr;
+ }
+
+ // integer math, even on 2s complement, allows stuff like
+ // x + 5 == 7
+ // =>
+ // x == 2
+ // TODO: templatize on type?
+ Expression* optimizeRelational(Binary* binary) {
+ // TODO: inequalities can also work, if the constants do not overflow
+ auto type = binary->right->type;
+ if (binary->op ==Abstract::getBinary(type, Abstract::Eq) ||
+ binary->op ==Abstract::getBinary(type, Abstract::Ne)) {
+ if (isIntegerType(binary->left->type)) {
+ if (auto* left = binary->left->dynCast<Binary>()) {
+ if (left->op == Abstract::getBinary(type, Abstract::Add) ||
+ left->op == Abstract::getBinary(type, Abstract::Sub)) {
+ if (auto* leftConst = left->right->dynCast<Const>()) {
+ if (auto* rightConst = binary->right->dynCast<Const>()) {
+ return combineRelationalConstants(binary, left, leftConst, nullptr, rightConst);
+ } else if (auto* rightBinary = binary->right->dynCast<Binary>()) {
+ if (rightBinary->op == Abstract::getBinary(type, Abstract::Add) ||
+ rightBinary->op == Abstract::getBinary(type, Abstract::Sub)) {
+ if (auto* rightConst = rightBinary->right->dynCast<Const>()) {
+ return combineRelationalConstants(binary, left, leftConst, rightBinary, rightConst);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ // given a relational binary with a const on both sides, combine the constants
+ // left is also a binary, and has a constant; right may be just a constant, in which
+ // case right is nullptr
+ Expression* combineRelationalConstants(Binary* binary, Binary* left, Const* leftConst, Binary* right, Const* rightConst) {
+ auto type = binary->right->type;
+ // we fold constants to the right
+ Literal extra = leftConst->value;
+ if (left->op == Abstract::getBinary(type, Abstract::Sub)) {
+ extra = extra.neg();
+ }
+ if (right && right->op == Abstract::getBinary(type, Abstract::Sub)) {
+ extra = extra.neg();
+ }
+ rightConst->value = rightConst->value.sub(extra);
+ binary->left = left->left;
+ return binary;
+ }
};
Pass *createOptimizeInstructionsPass() {