summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ir/bits.h11
-rw-r--r--src/passes/OptimizeInstructions.cpp90
-rw-r--r--test/lit/passes/optimize-instructions.wast496
-rw-r--r--test/passes/optimize-instructions_fuzz-exec.txt5
4 files changed, 539 insertions, 63 deletions
diff --git a/src/ir/bits.h b/src/ir/bits.h
index 96b36a846..25d80fba7 100644
--- a/src/ir/bits.h
+++ b/src/ir/bits.h
@@ -437,6 +437,17 @@ Index getMaxBits(Expression* curr,
}
}
+// As getMaxBits, but returns the minimum amount of bits.
+inline Index getMinBits(Expression* curr) {
+ if (auto* c = curr->dynCast<Const>()) {
+ // Constants are simple: the min and max are identical.
+ return getMaxBits(c);
+ }
+
+ // TODO: everything else
+ return 0;
+}
+
} // namespace wasm::Bits
#endif // wasm_ir_bits_h
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 9024d0cb6..9b207d6e7 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -1476,6 +1476,11 @@ struct OptimizeInstructions
curr, *getModule(), getPassOptions(), result);
}
+ Expression* getDroppedChildrenAndAppend(Expression* curr, Literal value) {
+ auto* result = Builder(*getModule()).makeConst(value);
+ return getDroppedChildrenAndAppend(curr, result);
+ }
+
void visitRefEq(RefEq* curr) {
// The types may prove that the same reference cannot appear on both sides.
auto leftType = curr->left->type;
@@ -1494,9 +1499,8 @@ struct OptimizeInstructions
// possibly appear on both sides is null, but one of the two is non-
// nullable, which rules that out. So there is no way that the same
// reference can appear on both sides.
- auto* result =
- Builder(*getModule()).makeConst(Literal::makeZero(Type::i32));
- replaceCurrent(getDroppedChildrenAndAppend(curr, result));
+ replaceCurrent(
+ getDroppedChildrenAndAppend(curr, Literal::makeZero(Type::i32)));
return;
}
@@ -1515,9 +1519,8 @@ struct OptimizeInstructions
// cases yet; the foldable case we do handle is the common one of the first
// child being a tee and the second a get of that tee. TODO)
if (areConsecutiveInputsEqualAndFoldable(curr->left, curr->right)) {
- auto* result =
- Builder(*getModule()).makeConst(Literal::makeOne(Type::i32));
- replaceCurrent(getDroppedChildrenAndAppend(curr, result));
+ replaceCurrent(
+ getDroppedChildrenAndAppend(curr, Literal::makeOne(Type::i32)));
return;
}
@@ -3965,6 +3968,81 @@ private:
return curr;
}
}
+
+ // Comparisons can sometimes be simplified depending on the number of
+ // bits, e.g. (unsigned)x > y must be true if x has strictly more bits.
+ // A common case is a constant on the right, e.g. (x & 255) < 256 must be
+ // true.
+ // TODO: use getMinBits in more places, see ideas in
+ // https://github.com/WebAssembly/binaryen/issues/2898
+ {
+ // Check if there is a nontrivial amount of bits on the left, which may
+ // provide enough to optimize.
+ auto leftMaxBits = Bits::getMaxBits(curr->left, this);
+ auto type = curr->left->type;
+ if (leftMaxBits < getBitsForType(type)) {
+ using namespace Abstract;
+ auto rightMinBits = Bits::getMinBits(curr->right);
+ auto rightIsNegative = rightMinBits == getBitsForType(type);
+ if (leftMaxBits < rightMinBits) {
+ // There are not enough bits on the left for it to be equal to the
+ // right, making various comparisons obviously false:
+ // x == y
+ // (unsigned)x > y
+ // (unsigned)x >= y
+ // and the same for signed, if y does not have the sign bit set
+ // (in that case, the comparison is effectively unsigned).
+ //
+ // TODO: In addition to leftMaxBits < rightMinBits, we could
+ // handle the reverse, and also special cases like all bits
+ // being 1 on the right, things like (x & 255) <= 255 -> 1
+ if (curr->op == Abstract::getBinary(type, Eq) ||
+ curr->op == Abstract::getBinary(type, GtU) ||
+ curr->op == Abstract::getBinary(type, GeU) ||
+ (!rightIsNegative &&
+ (curr->op == Abstract::getBinary(type, GtS) ||
+ curr->op == Abstract::getBinary(type, GeS)))) {
+ return getDroppedChildrenAndAppend(curr,
+ Literal::makeZero(Type::i32));
+ }
+
+ // And some are obviously true:
+ // x != y
+ // (unsigned)x < y
+ // (unsigned)x <= y
+ // and likewise for signed, as above.
+ if (curr->op == Abstract::getBinary(type, Ne) ||
+ curr->op == Abstract::getBinary(type, LtU) ||
+ curr->op == Abstract::getBinary(type, LeU) ||
+ (!rightIsNegative &&
+ (curr->op == Abstract::getBinary(type, LtS) ||
+ curr->op == Abstract::getBinary(type, LeS)))) {
+ return getDroppedChildrenAndAppend(curr,
+ Literal::makeOne(Type::i32));
+ }
+
+ // For truly signed comparisons, where y's sign bit is set, we can
+ // also infer some things, since we know y is signed but x is not
+ // (since x does not have enough bits for the sign bit to be set).
+ if (rightIsNegative) {
+ // (signed, non-negative)x > (negative)y => 1
+ // (signed, non-negative)x >= (negative)y => 1
+ if (curr->op == Abstract::getBinary(type, GtS) ||
+ curr->op == Abstract::getBinary(type, GeS)) {
+ return getDroppedChildrenAndAppend(curr,
+ Literal::makeOne(Type::i32));
+ }
+ // (signed, non-negative)x < (negative)y => 0
+ // (signed, non-negative)x <= (negative)y => 0
+ if (curr->op == Abstract::getBinary(type, LtS) ||
+ curr->op == Abstract::getBinary(type, LeS)) {
+ return getDroppedChildrenAndAppend(
+ curr, Literal::makeZero(Type::i32));
+ }
+ }
+ }
+ }
+ }
}
return nullptr;
}
diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast
index c75040a9f..df8037a72 100644
--- a/test/lit/passes/optimize-instructions.wast
+++ b/test/lit/passes/optimize-instructions.wast
@@ -152,9 +152,8 @@
)
)
;; CHECK: (func $eqz-gt_s (result i32)
- ;; CHECK-NEXT: (i32.le_u
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (i32.eqz
+ ;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $eqz-gt_s (result i32)
@@ -166,9 +165,8 @@
)
)
;; CHECK: (func $eqz-ge_s (result i32)
- ;; CHECK-NEXT: (i32.lt_u
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (i32.eqz
+ ;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $eqz-ge_s (result i32)
@@ -180,9 +178,8 @@
)
)
;; CHECK: (func $eqz-lt_s (result i32)
- ;; CHECK-NEXT: (i32.ge_u
+ ;; CHECK-NEXT: (i32.eqz
;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $eqz-lt_s (result i32)
@@ -194,9 +191,8 @@
)
)
;; CHECK: (func $eqz-le_s (result i32)
- ;; CHECK-NEXT: (i32.gt_u
+ ;; CHECK-NEXT: (i32.eqz
;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $eqz-le_s (result i32)
@@ -208,9 +204,8 @@
)
)
;; CHECK: (func $eqz-gt_u (result i32)
- ;; CHECK-NEXT: (i32.le_u
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (i32.eqz
+ ;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $eqz-gt_u (result i32)
@@ -222,9 +217,8 @@
)
)
;; CHECK: (func $eqz-ge_u (result i32)
- ;; CHECK-NEXT: (i32.lt_u
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (i32.const 2)
+ ;; CHECK-NEXT: (i32.eqz
+ ;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $eqz-ge_u (result i32)
@@ -236,9 +230,8 @@
)
)
;; CHECK: (func $eqz-lt_u (result i32)
- ;; CHECK-NEXT: (i32.ge_u
+ ;; CHECK-NEXT: (i32.eqz
;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $eqz-lt_u (result i32)
@@ -250,9 +243,8 @@
)
)
;; CHECK: (func $eqz-le_u (result i32)
- ;; CHECK-NEXT: (i32.gt_u
+ ;; CHECK-NEXT: (i32.eqz
;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $eqz-le_u (result i32)
@@ -463,10 +455,7 @@
)
)
;; CHECK: (func $eq-zero-lhs (result i32)
- ;; CHECK-NEXT: (i32.eq
- ;; CHECK-NEXT: (i32.const 0)
- ;; CHECK-NEXT: (i32.const 100)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
(func $eq-zero-lhs (result i32)
(i32.eq
@@ -497,10 +486,7 @@
)
)
;; CHECK: (func $eq-zero-lhs-i64 (result i32)
- ;; CHECK-NEXT: (i64.eq
- ;; CHECK-NEXT: (i64.const 0)
- ;; CHECK-NEXT: (i64.const 100)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
(func $eq-zero-lhs-i64 (result i32)
(i64.eq
@@ -1385,10 +1371,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (i32.lt_u
- ;; CHECK-NEXT: (i32.const 2000)
- ;; CHECK-NEXT: (i32.const 3000)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $and-pos1
@@ -12702,13 +12685,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (i32.eq
- ;; CHECK-NEXT: (i32.and
- ;; CHECK-NEXT: (local.get $x)
- ;; CHECK-NEXT: (i32.const 2147483647)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (i32.const -2147483648)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i64.shr_u
@@ -12756,13 +12733,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (i32.ge_s
- ;; CHECK-NEXT: (i32.and
- ;; CHECK-NEXT: (local.get $x)
- ;; CHECK-NEXT: (i32.const 2147483647)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (i32.const -7)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $unsigned-context (param $x i32) (param $y i64)
@@ -15314,13 +15285,7 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (i32.ne
- ;; CHECK-NEXT: (i32.shr_u
- ;; CHECK-NEXT: (local.get $x)
- ;; CHECK-NEXT: (i32.const 1)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (i32.const -1)
- ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $gt_u-added-constant (param $x i32)
@@ -15650,4 +15615,429 @@
)
)
)
+
+ ;; CHECK: (func $too-few-bits (param $x i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $too-few-bits (param $x i32)
+ ;; Comparison of something with at most 8 bits to something with more than
+ ;; 8. These must all be false.
+ (drop
+ (i32.eq
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 256)
+ )
+ )
+ (drop
+ (i32.gt_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 256)
+ )
+ )
+ (drop
+ (i32.gt_u
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 256)
+ )
+ )
+ (drop
+ (i32.ge_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 256)
+ )
+ )
+ (drop
+ (i32.ge_u
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 256)
+ )
+ )
+ ;; These are all true.
+ (drop
+ (i32.ne
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 256)
+ )
+ )
+ (drop
+ (i32.lt_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 256)
+ )
+ )
+ (drop
+ (i32.lt_u
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 256)
+ )
+ )
+ (drop
+ (i32.le_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 256)
+ )
+ )
+ (drop
+ (i32.le_u
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 256)
+ )
+ )
+ )
+
+ ;; CHECK: (func $too-few-bits-no (param $x i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.eq
+ ;; CHECK-NEXT: (i32.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.ne
+ ;; CHECK-NEXT: (i32.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.lt_u
+ ;; CHECK-NEXT: (i32.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.lt_u
+ ;; CHECK-NEXT: (i32.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.le_u
+ ;; CHECK-NEXT: (i32.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.le_u
+ ;; CHECK-NEXT: (i32.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.gt_u
+ ;; CHECK-NEXT: (i32.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.gt_u
+ ;; CHECK-NEXT: (i32.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.ge_u
+ ;; CHECK-NEXT: (i32.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.ge_u
+ ;; CHECK-NEXT: (i32.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i32.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $too-few-bits-no (param $x i32)
+ ;; Identical to the above, but the constant is changed from 256 to 255. We
+ ;; cannot optimize here: the number of bits is the same and we can't infer
+ ;; anything.
+ (drop
+ (i32.eq
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 255)
+ )
+ )
+ (drop
+ (i32.ne
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 255)
+ )
+ )
+ (drop
+ (i32.lt_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 255)
+ )
+ )
+ (drop
+ (i32.lt_u
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 255)
+ )
+ )
+ (drop
+ (i32.le_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 255)
+ )
+ )
+ (drop
+ (i32.le_u
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 255)
+ )
+ )
+ (drop
+ (i32.gt_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 255)
+ )
+ )
+ (drop
+ (i32.gt_u
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 255)
+ )
+ )
+ (drop
+ (i32.ge_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 255)
+ )
+ )
+ (drop
+ (i32.ge_u
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 255)
+ )
+ )
+ )
+
+ ;; CHECK: (func $too-few-bits-signed (param $x i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.ne
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i32.const -2147483648)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $too-few-bits-signed (param $x i32)
+ ;; As above, but now using only signed operations and the constant on the
+ ;; right has the sign bit set. The left side is non-negative, which lets us
+ ;; infer the results here.
+ ;; These are all false:
+ (drop
+ (i32.lt_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 0x80000000) ;; -2147483648
+ )
+ )
+ (drop
+ (i32.le_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 0x80000000) ;; -2147483648
+ )
+ )
+ ;; These are all true:
+ (drop
+ (i32.gt_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 0x80000000) ;; -2147483648
+ )
+ )
+ (drop
+ (i32.ge_s
+ (i32.and
+ (local.get $x)
+ (i32.const 255)
+ )
+ (i32.const 0x80000000) ;; -2147483648
+ )
+ )
+ ;; This cannot be inferred, as the left has too many possible bits (so it
+ ;; may have the sign bit set).
+ (drop
+ (i32.gt_s
+ (local.get $x)
+ (i32.const 0x80000000) ;; -2147483648
+ )
+ )
+ )
+
+ ;; CHECK: (func $too-few-bits-i64 (param $x i64)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i64.eq
+ ;; CHECK-NEXT: (i64.and
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (i64.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (i64.const 255)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $too-few-bits-i64 (param $x i64)
+ ;; As above, but with i64 values. We can infer 0 here.
+ (drop
+ (i64.eq
+ (i64.and
+ (local.get $x)
+ (i64.const 255)
+ )
+ (i64.const 256)
+ )
+ )
+ ;; The constant is now 255 and we cannot optimize here.
+ (drop
+ (i64.eq
+ (i64.and
+ (local.get $x)
+ (i64.const 255)
+ )
+ (i64.const 255)
+ )
+ )
+ )
)
diff --git a/test/passes/optimize-instructions_fuzz-exec.txt b/test/passes/optimize-instructions_fuzz-exec.txt
index 5a8ae7e5f..16b17cf26 100644
--- a/test/passes/optimize-instructions_fuzz-exec.txt
+++ b/test/passes/optimize-instructions_fuzz-exec.txt
@@ -316,10 +316,7 @@
)
)
(call $log
- (i32.eq
- (i32.const 8)
- (i32.const -2147483648)
- )
+ (i32.const 0)
)
)
(func $shift (param $0 i32)