diff options
-rw-r--r-- | src/literal.h | 85 | ||||
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 23 | ||||
-rw-r--r-- | test/passes/optimize-instructions_all-features.txt | 83 | ||||
-rw-r--r-- | test/passes/optimize-instructions_all-features.wast | 81 |
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) )) |