summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/literal.h85
-rw-r--r--src/passes/OptimizeInstructions.cpp23
-rw-r--r--test/passes/optimize-instructions_all-features.txt83
-rw-r--r--test/passes/optimize-instructions_all-features.wast81
4 files changed, 252 insertions, 20 deletions
diff --git a/src/literal.h b/src/literal.h
index eedccabcc..33300c920 100644
--- a/src/literal.h
+++ b/src/literal.h
@@ -96,7 +96,57 @@ public:
}
return false;
}
+ bool isZero() const {
+ switch (type.getBasic()) {
+ case Type::i32:
+ return i32 == 0;
+ case Type::i64:
+ return i64 == 0LL;
+ case Type::f32:
+ return bit_cast<float>(i32) == 0.0f;
+ case Type::f64:
+ return bit_cast<double>(i64) == 0.0;
+ case Type::v128: {
+ uint8_t zeros[16] = {0};
+ return memcmp(&v128, zeros, 16) == 0;
+ }
+ default:
+ WASM_UNREACHABLE("unexpected type");
+ }
+ }
+ bool isSignedMin() const {
+ switch (type.getBasic()) {
+ case Type::i32:
+ return i32 == std::numeric_limits<int32_t>::min();
+ case Type::i64:
+ return i64 == std::numeric_limits<int64_t>::min();
+ default:
+ WASM_UNREACHABLE("unexpected type");
+ }
+ }
+ bool isSignedMax() const {
+ switch (type.getBasic()) {
+ case Type::i32:
+ return i32 == std::numeric_limits<int32_t>::max();
+ case Type::i64:
+ return i64 == std::numeric_limits<int64_t>::max();
+ default:
+ WASM_UNREACHABLE("unexpected type");
+ }
+ }
+ bool isUnsignedMax() const {
+ switch (type.getBasic()) {
+ case Type::i32:
+ return uint32_t(i32) == std::numeric_limits<uint32_t>::max();
+ case Type::i64:
+ return uint64_t(i64) == std::numeric_limits<uint64_t>::max();
+ default:
+ WASM_UNREACHABLE("unexpected type");
+ }
+ }
+ static Literals makeZero(Type type);
+ static Literal makeSingleZero(Type type);
static Literal makeFromInt32(int32_t x, Type type) {
switch (type.getBasic()) {
case Type::i32:
@@ -116,7 +166,6 @@ public:
WASM_UNREACHABLE("unexpected type");
}
}
-
static Literal makeFromUInt64(uint64_t x, Type type) {
switch (type.getBasic()) {
case Type::i32:
@@ -131,10 +180,36 @@ public:
WASM_UNREACHABLE("unexpected type");
}
}
-
- static Literals makeZero(Type type);
- static Literal makeSingleZero(Type type);
-
+ static Literal makeSignedMin(Type type) {
+ switch (type.getBasic()) {
+ case Type::i32:
+ return Literal(std::numeric_limits<int32_t>::min());
+ case Type::i64:
+ return Literal(std::numeric_limits<int64_t>::min());
+ default:
+ WASM_UNREACHABLE("unexpected type");
+ }
+ }
+ static Literal makeSignedMax(Type type) {
+ switch (type.getBasic()) {
+ case Type::i32:
+ return Literal(std::numeric_limits<int32_t>::max());
+ case Type::i64:
+ return Literal(std::numeric_limits<int64_t>::max());
+ default:
+ WASM_UNREACHABLE("unexpected type");
+ }
+ }
+ static Literal makeUnsignedMax(Type type) {
+ switch (type.getBasic()) {
+ case Type::i32:
+ return Literal(std::numeric_limits<uint32_t>::max());
+ case Type::i64:
+ return Literal(std::numeric_limits<uint64_t>::max());
+ default:
+ WASM_UNREACHABLE("unexpected type");
+ }
+ }
static Literal makeNull(Type type) {
assert(type.isNullable());
return Literal(type);
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 79315ac6d..de96e418e 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -298,15 +298,16 @@ struct OptimizeInstructions
}
}
{
- // eqz((signed)x % C_pot) => eqz(x & (C_pot - 1))
+ // eqz((signed)x % C_pot) => eqz(x & (abs(C_pot) - 1))
Const* c;
Binary* inner;
if (matches(curr,
unary(Abstract::EqZ,
binary(&inner, Abstract::RemS, any(), ival(&c)))) &&
- Bits::isPowerOf2((uint64_t)c->value.getInteger())) {
+ !c->value.isSignedMin() &&
+ Bits::isPowerOf2(c->value.abs().getInteger())) {
inner->op = Abstract::getBinary(c->type, Abstract::And);
- c->value = c->value.sub(Literal::makeFromInt32(1, c->type));
+ c->value = c->value.abs().sub(Literal::makeFromInt32(1, c->type));
return curr;
}
}
@@ -1307,7 +1308,16 @@ private:
right->value = Literal::makeSingleZero(type);
return right;
}
- // (signed)x % C_pot != 0 ==> x & (C_pot - 1) != 0
+ {
+ // (signed)x % (i32|i64).min_s ==> (x & (i32|i64).max_s)
+ if (matches(curr, binary(Abstract::RemS, any(&left), ival())) &&
+ right->value.isSignedMin()) {
+ curr->op = Abstract::getBinary(type, Abstract::And);
+ right->value = Literal::makeSignedMax(type);
+ return curr;
+ }
+ }
+ // (signed)x % C_pot != 0 ==> (x & (abs(C_pot) - 1)) != 0
{
Const* c;
Binary* inner;
@@ -1315,9 +1325,10 @@ private:
binary(Abstract::Ne,
binary(&inner, Abstract::RemS, any(), ival(&c)),
ival(0))) &&
- Bits::isPowerOf2((uint64_t)c->value.getInteger())) {
+ !c->value.isSignedMin() &&
+ Bits::isPowerOf2(c->value.abs().getInteger())) {
inner->op = Abstract::getBinary(c->type, Abstract::And);
- c->value = c->value.sub(Literal::makeFromInt32(1, c->type));
+ c->value = c->value.abs().sub(Literal::makeFromInt32(1, c->type));
return curr;
}
}
diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt
index ee6b1162a..272cce240 100644
--- a/test/passes/optimize-instructions_all-features.txt
+++ b/test/passes/optimize-instructions_all-features.txt
@@ -2737,13 +2737,25 @@
)
(unreachable)
)
- (func $srem-by-1 (param $x i32) (param $y i64)
+ (func $srem-by-const (param $x i32) (param $y i64)
(drop
(i32.const 0)
)
(drop
(i64.const 0)
)
+ (drop
+ (i32.and
+ (local.get $x)
+ (i32.const 2147483647)
+ )
+ )
+ (drop
+ (i64.and
+ (local.get $y)
+ (i64.const 9223372036854775807)
+ )
+ )
)
(func $srem-by-pot-eq-ne-zero (param $x i32) (param $y i64)
(drop
@@ -2774,11 +2786,43 @@
(i64.eqz
(i64.and
(local.get $y)
+ (i64.const 3)
+ )
+ )
+ )
+ (drop
+ (i32.eqz
+ (i32.and
+ (local.get $x)
+ (i32.const 3)
+ )
+ )
+ )
+ (drop
+ (i64.eqz
+ (i64.and
+ (local.get $y)
(i64.const 1)
)
)
)
(drop
+ (i32.eqz
+ (i32.and
+ (local.get $x)
+ (i32.const 3)
+ )
+ )
+ )
+ (drop
+ (i64.eqz
+ (i64.and
+ (local.get $y)
+ (i64.const 3)
+ )
+ )
+ )
+ (drop
(i32.ne
(i32.and
(local.get $x)
@@ -2797,17 +2841,48 @@
)
(drop
(i32.eqz
- (i32.rem_s
+ (i32.const 0)
+ )
+ )
+ (drop
+ (i32.eqz
+ (i32.and
(local.get $x)
- (i32.const 3)
+ (i32.const 2147483647)
+ )
+ )
+ )
+ (drop
+ (i32.ne
+ (i32.and
+ (local.get $x)
+ (i32.const 2147483647)
+ )
+ (i32.const 0)
+ )
+ )
+ (drop
+ (i64.eqz
+ (i64.and
+ (local.get $y)
+ (i64.const 9223372036854775807)
)
)
)
(drop
+ (i64.ne
+ (i64.and
+ (local.get $y)
+ (i64.const 9223372036854775807)
+ )
+ (i64.const 0)
+ )
+ )
+ (drop
(i32.eqz
(i32.rem_s
(local.get $x)
- (i32.const -4)
+ (i32.const 3)
)
)
)
diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast
index 71646de93..05e0e37c3 100644
--- a/test/passes/optimize-instructions_all-features.wast
+++ b/test/passes/optimize-instructions_all-features.wast
@@ -3093,7 +3093,7 @@
)
(unreachable)
)
- (func $srem-by-1 (param $x i32) (param $y i64)
+ (func $srem-by-const (param $x i32) (param $y i64)
;; (signed)x % 1
(drop (i32.rem_s
(local.get $x)
@@ -3103,6 +3103,16 @@
(local.get $y)
(i64.const 1)
))
+ ;; (signed)x % 0x80000000 -> x & 0x7FFFFFFF
+ (drop (i32.rem_s
+ (local.get $x)
+ (i32.const 0x80000000)
+ ))
+ ;; (signed)x % 0x8000000000000000 -> x & 0x7FFFFFFFFFFFFFFF
+ (drop (i64.rem_s
+ (local.get $y)
+ (i64.const 0x8000000000000000)
+ ))
)
(func $srem-by-pot-eq-ne-zero (param $x i32) (param $y i64)
;; eqz((signed)x % 4)
@@ -3118,6 +3128,19 @@
(i64.const 4)
)
))
+ ;; eqz((signed)x % -4)
+ (drop (i32.eqz
+ (i32.rem_s
+ (local.get $x)
+ (i32.const -4)
+ )
+ ))
+ (drop (i64.eqz
+ (i64.rem_s
+ (local.get $y)
+ (i64.const -4)
+ )
+ ))
;; (signed)x % 4 == 0
(drop (i32.eq
(i32.rem_s
@@ -3133,7 +3156,22 @@
)
(i64.const 0)
))
- ;; ;; (signed)x % 2 != 0
+ ;; (signed)x % -4 == 0
+ (drop (i32.eq
+ (i32.rem_s
+ (local.get $x)
+ (i32.const -4)
+ )
+ (i32.const 0)
+ ))
+ (drop (i64.eq
+ (i64.rem_s
+ (local.get $y)
+ (i64.const -4)
+ )
+ (i64.const 0)
+ ))
+ ;; (signed)x % 2 != 0
(drop (i32.ne
(i32.rem_s
(local.get $x)
@@ -3148,18 +3186,51 @@
)
(i64.const 0)
))
- ;; ;;
+ ;; (signed)x % -1 == 0 -> 0 == 0
(drop (i32.eq
(i32.rem_s
(local.get $x)
- (i32.const 3) ;; skip
+ (i32.const -1)
+ )
+ (i32.const 0)
+ ))
+ ;; (signed)x % 0x80000000 == 0
+ (drop (i32.eq
+ (i32.rem_s
+ (local.get $x)
+ (i32.const 0x80000000)
+ )
+ (i32.const 0)
+ ))
+ ;; (signed)x % 0x80000000 != 0
+ (drop (i32.ne
+ (i32.rem_s
+ (local.get $x)
+ (i32.const 0x80000000)
)
(i32.const 0)
))
+ ;; (signed)x % 0x8000000000000000 == 0
+ (drop (i64.eq
+ (i64.rem_s
+ (local.get $y)
+ (i64.const 0x8000000000000000)
+ )
+ (i64.const 0)
+ ))
+ ;; (signed)x % 0x8000000000000000 != 0
+ (drop (i64.ne
+ (i64.rem_s
+ (local.get $y)
+ (i64.const 0x8000000000000000)
+ )
+ (i64.const 0)
+ ))
+ ;;
(drop (i32.eq
(i32.rem_s
(local.get $x)
- (i32.const -4) ;; skip
+ (i32.const 3) ;; skip
)
(i32.const 0)
))