summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/OptimizeInstructions.cpp109
1 files changed, 86 insertions, 23 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index de46f155a..bb4748a97 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -263,6 +263,36 @@ static Index getSignExtBits(Expression* curr) {
return 32 - curr->cast<Binary>()->right->cast<Const>()->value.geti32();
}
+// Check if an expression is almost a sign-extend: perhaps the inner shift
+// is too large. We can split the shifts in that case, which is sometimes
+// useful (e.g. if we can remove the signext)
+static Expression* getAlmostSignExt(Expression* curr) {
+ if (auto* outer = curr->dynCast<Binary>()) {
+ if (outer->op == ShrSInt32) {
+ if (auto* outerConst = outer->right->dynCast<Const>()) {
+ if (auto* inner = outer->left->dynCast<Binary>()) {
+ if (inner->op == ShlInt32) {
+ if (auto* innerConst = inner->right->dynCast<Const>()) {
+ if (outerConst->value.leU(innerConst->value).geti32()) {
+ return inner->left;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+// gets the size of the almost sign-extended value, as well as the
+// extra shifts, if any
+static Index getAlmostSignExtBits(Expression* curr, Index& extraShifts) {
+ extraShifts = curr->cast<Binary>()->left->cast<Binary>()->right->cast<Const>()->value.geti32() -
+ curr->cast<Binary>()->right->cast<Const>()->value.geti32();
+ return getSignExtBits(curr);
+}
+
// get a mask to keep only the low # of bits
static int32_t lowBitMask(int32_t bits) {
uint32_t ret = -1;
@@ -321,17 +351,18 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
std::swap(binary->left, binary->right);
}
}
- if (auto* ext = getSignExt(binary)) {
- auto bits = getSignExtBits(binary);
+ if (auto* ext = getAlmostSignExt(binary)) {
+ Index extraShifts;
+ auto bits = getAlmostSignExtBits(binary, extraShifts);
auto* load = ext->dynCast<Load>();
// pattern match a load of 8 bits and a sign extend using a shl of 24 then shr_s of 24 as well, etc.
if (load && ((load->bytes == 1 && bits == 8) || (load->bytes == 2 && bits == 16))) {
load->signed_ = true;
- return load;
+ return removeAlmostSignExt(binary);
}
// if the sign-extend input cannot have a sign bit, we don't need it
- if (getMaxBits(ext) < bits) {
- return ext;
+ if (getMaxBits(ext) + extraShifts < bits) {
+ return removeAlmostSignExt(binary);
}
} else if (binary->op == EqInt32) {
if (auto* c = binary->right->dynCast<Const>()) {
@@ -359,29 +390,46 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
// note that both left and right may be consts, but then we let precompute compute the constant result
} else if (binary->op == AddInt32 || binary->op == SubInt32) {
return optimizeAddedConstants(binary);
- } else if (binary->op == AndInt32) {
- if (auto* right = binary->right->dynCast<Const>()) {
- if (right->type == i32) {
- auto mask = right->value.geti32();
- // and with -1 does nothing (common in asm.js output)
- if (mask == -1) {
- return binary->left;
+ }
+ // a bunch of operations on a constant right side can be simplified
+ if (auto* right = binary->right->dynCast<Const>()) {
+ if (binary->op == AndInt32) {
+ auto mask = right->value.geti32();
+ // and with -1 does nothing (common in asm.js output)
+ if (mask == -1) {
+ return binary->left;
+ }
+ // small loads do not need to be masted, the load itself masks
+ if (auto* load = binary->left->dynCast<Load>()) {
+ if ((load->bytes == 1 && mask == 0xff) ||
+ (load->bytes == 2 && mask == 0xffff)) {
+ load->signed_ = false;
+ return load;
}
- // small loads do not need to be masted, the load itself masks
- if (auto* load = binary->left->dynCast<Load>()) {
- if ((load->bytes == 1 && mask == 0xff) ||
- (load->bytes == 2 && mask == 0xffff)) {
- load->signed_ = false;
- return load;
+ } else if (mask == 1 && Properties::emitsBoolean(binary->left)) {
+ // (bool) & 1 does not need the outer mask
+ return binary->left;
+ }
+ }
+ // the square of some operations can be merged
+ if (auto* left = binary->left->dynCast<Binary>()) {
+ if (left->op == binary->op) {
+ if (auto* leftRight = left->right->dynCast<Const>()) {
+ if (left->op == AndInt32) {
+ leftRight->value = leftRight->value.and_(right->value);
+ return left;
+ } else if (left->op == OrInt32) {
+ leftRight->value = leftRight->value.or_(right->value);
+ return left;
+ } else if (left->op == ShlInt32 || left->op == ShrUInt32 || left->op == ShrSInt32) {
+ leftRight->value = leftRight->value.add(right->value);
+ return left;
}
- } else if (mask == 1 && Properties::emitsBoolean(binary->left)) {
- // (bool) & 1 does not need the outer mask
- return binary->left;
}
}
}
- return conditionalizeExpensiveOnBitwise(binary);
- } else if (binary->op == OrInt32) {
+ }
+ if (binary->op == AndInt32 || binary->op == OrInt32) {
return conditionalizeExpensiveOnBitwise(binary);
}
} else if (auto* unary = curr->dynCast<Unary>()) {
@@ -685,6 +733,21 @@ private:
Builder builder(*getModule());
return builder.makeBinary(AndInt32, curr, builder.makeConst(Literal(lowBitMask(bits))));
}
+
+ // given an "almost" sign extend - either a proper one, or it
+ // has too many shifts left - we remove the sig extend. If there are
+ // too many shifts, we split the shifts first, so this removes the
+ // two sign extend shifts and adds one (smaller one)
+ Expression* removeAlmostSignExt(Binary* outer) {
+ auto* inner = outer->left->cast<Binary>();
+ auto* outerConst = outer->right->cast<Const>();
+ auto* innerConst = inner->right->cast<Const>();
+ auto* value = inner->left;
+ if (outerConst->value == innerConst->value) return value;
+ // add a shift, by reusing the existing node
+ innerConst->value = innerConst->value.sub(outerConst->value);
+ return inner;
+ }
};
Pass *createOptimizeInstructionsPass() {