From b67c4ac200044cd534551d9625e29c993c16d0ec Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Wed, 16 Apr 2025 15:29:54 +0800 Subject: [PATCH 01/31] opt for #7481 --- src/passes/OptimizeInstructions.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 8c13663deba..af24e661e01 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -837,6 +837,9 @@ struct OptimizeInstructions if (auto* ret = combineAnd(curr)) { return replaceCurrent(ret); } + if (auto* ret = optimizeAndBooleanWithEvenConstant(curr)) { + return replaceCurrent(ret); + } } // for or, we can potentially combine if (curr->op == OrInt32) { @@ -3549,6 +3552,29 @@ struct OptimizeInstructions return nullptr; } + // Bitwise AND of a boolean result (0 or 1) with any value + // whose LSB is guaranteed to be 0 always yields 0. + Expression* optimizeAndBooleanWithEvenConstant(Binary* curr) { + assert(curr->op == AndInt32); + + using namespace Abstract; + using namespace Match; + auto leftMaxBits = Bits::getMaxBits(curr->left, this); + auto type = curr->left->type; + if (curr->op == Abstract::getBinary(type, And)) { + if (leftMaxBits == 1) { + // boolean & (No overlap with boolean's LSB) ==> 0 + if (auto* c = curr->right->dynCast()) { + if ((1 & c->value.getInteger()) == 0) { + replaceCurrent(getDroppedChildrenAndAppend( + curr, LiteralUtils::makeZero(c->value.type, *getModule()))); + } + } + } + } + return nullptr; + } + // We can combine `or` operations, e.g. // (x > y) | (x == y) ==> x >= y // (x != 0) | (y != 0) ==> (x | y) != 0 From 340fd1b579ed9db4474a3204e453f34c6b696c61 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Wed, 16 Apr 2025 15:29:59 +0800 Subject: [PATCH 02/31] tests --- test/lit/passes/optimize-instructions-mvp.wast | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 9f505a7586f..cc17826ba1b 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -1442,10 +1442,7 @@ ) ;; CHECK: (func $canonicalize-consts-vars (param $x i32) (param $y i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.and @@ -7018,12 +7015,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.eqz From 6aab02966d173b4ac580b382a16344a6b736bad7 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Sat, 19 Apr 2025 14:10:57 +0800 Subject: [PATCH 03/31] refactor to be generalized --- src/passes/OptimizeInstructions.cpp | 42 ++++++++++++++++++----------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index af24e661e01..7478c0b60d8 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -837,7 +837,7 @@ struct OptimizeInstructions if (auto* ret = combineAnd(curr)) { return replaceCurrent(ret); } - if (auto* ret = optimizeAndBooleanWithEvenConstant(curr)) { + if (auto* ret = optimizeAndNoOverlappingBits(curr)) { return replaceCurrent(ret); } } @@ -3552,26 +3552,36 @@ struct OptimizeInstructions return nullptr; } - // Bitwise AND of a boolean result (0 or 1) with any value - // whose LSB is guaranteed to be 0 always yields 0. - Expression* optimizeAndBooleanWithEvenConstant(Binary* curr) { - assert(curr->op == AndInt32); - + // Bitwise AND of a value with bits in [0, n) and a constant with no bits in + // [0, n) always yields 0. Replace with zero. + Expression* optimizeAndNoOverlappingBits(Binary* curr) { using namespace Abstract; using namespace Match; - auto leftMaxBits = Bits::getMaxBits(curr->left, this); + auto type = curr->left->type; - if (curr->op == Abstract::getBinary(type, And)) { - if (leftMaxBits == 1) { - // boolean & (No overlap with boolean's LSB) ==> 0 - if (auto* c = curr->right->dynCast()) { - if ((1 & c->value.getInteger()) == 0) { - replaceCurrent(getDroppedChildrenAndAppend( - curr, LiteralUtils::makeZero(c->value.type, *getModule()))); - } - } + assert(curr->op == getBinary(type, And)); + + auto* left = curr->left; + auto* right = curr->right; + auto leftMaxBits = Bits::getMaxBits(left, this); + + if (leftMaxBits == 0) { + // Left is always zero, result is zero. + replaceCurrent(getDroppedChildrenAndAppend( + curr, LiteralUtils::makeZero(type, *getModule()))); + return nullptr; + } + + uint64_t mask = (1ULL << leftMaxBits) - 1; + + if (auto* c = right->dynCast()) { + uint64_t constantValue = c->value.getInteger(); + if ((constantValue & mask) == 0) { + replaceCurrent(getDroppedChildrenAndAppend( + curr, LiteralUtils::makeZero(type, *getModule()))); } } + return nullptr; } From 11992ac75c8793add6cef536d83ed7ee21615cef Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Sat, 19 Apr 2025 14:11:12 +0800 Subject: [PATCH 04/31] update test due to refactoring --- test/lit/passes/optimize-instructions-mvp.wast | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index cc17826ba1b..324ee006a1e 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -2863,10 +2863,7 @@ ) ) ;; CHECK: (func $sext-24-and-127-128 (result i32) - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.const 127) - ;; CHECK-NEXT: (i32.const 128) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) (func $sext-24-and-127-128 (result i32) (i32.shr_s From bfdc24c0d50855b2d6a64508f806f3d234899ae3 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 24 Apr 2025 18:52:39 +0800 Subject: [PATCH 05/31] No need for special-cased 0 --- src/passes/OptimizeInstructions.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 7478c0b60d8..31941581a7f 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3565,15 +3565,7 @@ struct OptimizeInstructions auto* right = curr->right; auto leftMaxBits = Bits::getMaxBits(left, this); - if (leftMaxBits == 0) { - // Left is always zero, result is zero. - replaceCurrent(getDroppedChildrenAndAppend( - curr, LiteralUtils::makeZero(type, *getModule()))); - return nullptr; - } - uint64_t mask = (1ULL << leftMaxBits) - 1; - if (auto* c = right->dynCast()) { uint64_t constantValue = c->value.getInteger(); if ((constantValue & mask) == 0) { From 27ac17f1ce1e3d183359e2a9b14221cdd1f976d4 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 24 Apr 2025 18:54:13 +0800 Subject: [PATCH 06/31] Avoid redundant replaceCurrent --- src/passes/OptimizeInstructions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 31941581a7f..28cbf89e451 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3569,8 +3569,8 @@ struct OptimizeInstructions if (auto* c = right->dynCast()) { uint64_t constantValue = c->value.getInteger(); if ((constantValue & mask) == 0) { - replaceCurrent(getDroppedChildrenAndAppend( - curr, LiteralUtils::makeZero(type, *getModule()))); + getDroppedChildrenAndAppend(curr, + LiteralUtils::makeZero(type, *getModule())); } } From 2f2dbfb402f7864ef02c49806b50348074aa5fed Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 24 Apr 2025 19:24:28 +0800 Subject: [PATCH 07/31] And is commutative --- src/passes/OptimizeInstructions.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 28cbf89e451..109b41b477b 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3563,14 +3563,26 @@ struct OptimizeInstructions auto* left = curr->left; auto* right = curr->right; - auto leftMaxBits = Bits::getMaxBits(left, this); - uint64_t mask = (1ULL << leftMaxBits) - 1; + // Check right as constant and left's max bits + auto leftMaxBits = Bits::getMaxBits(left, this); + uint64_t maskLeft = (1ULL << leftMaxBits) - 1; if (auto* c = right->dynCast()) { uint64_t constantValue = c->value.getInteger(); - if ((constantValue & mask) == 0) { - getDroppedChildrenAndAppend(curr, - LiteralUtils::makeZero(type, *getModule())); + if ((constantValue & maskLeft) == 0) { + return getDroppedChildrenAndAppend( + curr, LiteralUtils::makeZero(type, *getModule())); + } + } + + // Check left as constant and right's max bits + auto rightMaxBits = Bits::getMaxBits(right, this); + uint64_t maskRight = (1ULL << rightMaxBits) - 1; + if (auto* c = left->dynCast()) { + uint64_t constantValue = c->value.getInteger(); + if ((constantValue & maskRight) == 0) { + return getDroppedChildrenAndAppend( + curr, LiteralUtils::makeZero(type, *getModule())); } } From a8216a0fff0eb71f4b4ed076beed8fb9ac0d56ae Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 24 Apr 2025 19:25:14 +0800 Subject: [PATCH 08/31] Update test --- test/lit/passes/optimize-instructions-mvp.wast | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 324ee006a1e..7bf78c39172 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -1371,10 +1371,7 @@ ;; CHECK-NEXT: (i32.const 100) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.const 100) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $and-neg1 @@ -1393,10 +1390,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.const 100) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) @@ -1445,6 +1439,9 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.and ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (i32.const 3) @@ -1479,6 +1476,7 @@ ;; CHECK-NEXT: ) (func $canonicalize-consts-vars (param $x i32) (param $y i32) (drop (i32.and (i32.const 1) (i32.const 2))) + (drop (i32.and (i32.const 2) (i32.const 1))) (drop (i32.and (local.get $x) (i32.const 3))) (drop (i32.and (i32.const 4) (local.get $x))) (drop (i32.and (local.get $x) (local.get $y))) From 2bbd8471ffb23efaa80527441d7b7b21a7ceb92e Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 24 Apr 2025 19:35:34 +0800 Subject: [PATCH 09/31] Update test --- test/lit/passes/optimize-instructions-mvp.wast | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 7bf78c39172..94904919749 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -2860,15 +2860,18 @@ (i32.const 24) ) ) - ;; CHECK: (func $sext-24-and-127-128 (result i32) - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK: (func $sext-24-and-127-unknown (param $x i32) (result i32) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 127) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $sext-24-and-127-128 (result i32) + (func $sext-24-and-127-unknown (param $x i32) (result i32) (i32.shr_s (i32.shl (i32.and ;; takes the min, here it is ok (i32.const 127) - (i32.const 128) + (local.get $x) ) (i32.const 24) ) From dad3fd5b846651ae1fff79657af5af2eb4a54c2f Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 24 Apr 2025 19:42:00 +0800 Subject: [PATCH 10/31] Update test --- test/lit/passes/optimize-instructions-mvp.wast | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 94904919749..bbbda1c1019 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -6967,7 +6967,7 @@ ) ) ) - ;; CHECK: (func $de-morgan-2 (param $x i32) (param $y i32) + ;; CHECK: (func $de-morgan-2 (param $x i32) (param $y i32) (param $z i64) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.eqz ;; CHECK-NEXT: (i32.or @@ -7013,7 +7013,14 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (local.get $z) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.eqz @@ -7021,7 +7028,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $de-morgan-2 (param $x i32) (param $y i32) + (func $de-morgan-2 (param $x i32) (param $y i32) (param $z i64) (drop (i32.and (i32.eqz (local.get $x)) (i32.eqz (local.get $y))) ) @@ -7038,7 +7045,7 @@ (i32.and (local.get $x) (i32.eqz (local.get $y))) ) (drop - (i32.and (i32.eqz (local.get $x)) (i32.wrap_i64 (i64.const 2))) + (i32.and (i32.eqz (local.get $x)) (i32.wrap_i64 (local.get $z))) ) (drop (i32.and (i32.wrap_i64 (i64.const 1)) (i32.eqz (local.get $y))) From 51196f865c45f7f19ab7e1b13d900017ca2075de Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 24 Apr 2025 20:57:18 +0800 Subject: [PATCH 11/31] Deal with cornor case --- src/passes/OptimizeInstructions.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 109b41b477b..9a83adc76f9 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -832,11 +832,13 @@ struct OptimizeInstructions return replaceCurrent(ret); } } - if (curr->op == AndInt32 || curr->op == OrInt32) { + if (curr->op == AndInt32 || curr->op == AndInt64 || curr->op == OrInt32) { if (curr->op == AndInt32) { if (auto* ret = combineAnd(curr)) { return replaceCurrent(ret); } + } + if (curr->op == AndInt32 || curr->op == AndInt64) { if (auto* ret = optimizeAndNoOverlappingBits(curr)) { return replaceCurrent(ret); } @@ -3555,34 +3557,43 @@ struct OptimizeInstructions // Bitwise AND of a value with bits in [0, n) and a constant with no bits in // [0, n) always yields 0. Replace with zero. Expression* optimizeAndNoOverlappingBits(Binary* curr) { + assert(curr->op == AndInt32 || curr->op == AndInt64); + using namespace Abstract; using namespace Match; - auto type = curr->left->type; - assert(curr->op == getBinary(type, And)); - auto* left = curr->left; auto* right = curr->right; // Check right as constant and left's max bits auto leftMaxBits = Bits::getMaxBits(left, this); - uint64_t maskLeft = (1ULL << leftMaxBits) - 1; + uint64_t maskLeft; + if (leftMaxBits == 64) { + maskLeft = 0xffffffffffffffffULL; // All bits set for 64-bit case + } else { + maskLeft = (1ULL << leftMaxBits) - 1; + } if (auto* c = right->dynCast()) { uint64_t constantValue = c->value.getInteger(); if ((constantValue & maskLeft) == 0) { return getDroppedChildrenAndAppend( - curr, LiteralUtils::makeZero(type, *getModule())); + curr, LiteralUtils::makeZero(curr->left->type, *getModule())); } } // Check left as constant and right's max bits auto rightMaxBits = Bits::getMaxBits(right, this); - uint64_t maskRight = (1ULL << rightMaxBits) - 1; + uint64_t maskRight; + if (rightMaxBits == 64) { + maskRight = 0xffffffffffffffffULL; // All bits set for 64-bit case + } else { + maskRight = (1ULL << rightMaxBits) - 1; + } if (auto* c = left->dynCast()) { uint64_t constantValue = c->value.getInteger(); if ((constantValue & maskRight) == 0) { return getDroppedChildrenAndAppend( - curr, LiteralUtils::makeZero(type, *getModule())); + curr, LiteralUtils::makeZero(curr->right->type, *getModule())); } } From 131a423d5054eb52a5ee127b4b35d1098bb75413 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 24 Apr 2025 20:57:40 +0800 Subject: [PATCH 12/31] Add cornor case --- .../lit/passes/optimize-instructions-mvp.wast | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index bbbda1c1019..1eb688d3732 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -17770,4 +17770,124 @@ (i32.const 1) ) ) + ;; CHECK: (func $no-overlapping-bits-corner-case (param $x i32) (param $y i64) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const -2147483647) + ;; CHECK-NEXT: (i32.const 2147483647) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (i64.const 2147483649) + ;; CHECK-NEXT: (i64.const 2147483647) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $no-overlapping-bits-corner-case (param $x i32) (param $y i64) + ;; optimizeAndNoOverlappingBits will simplify AND operations where + ;; Bitwise AND of a value with bits in [0, n) and a constant with no bits in + ;; [0, n) always yields 0. Replace with zero. + ;; Note: after swapping the operands, it also satisfies the commutative law. + + ;; Unknown, not optimized + (drop + (i32.and + (local.get $x) + (i32.const 1) + ) + ) + (drop + (i64.and + (local.get $y) + (i64.const 1) + ) + ) + (drop + (i32.and + (i32.const 1) + (local.get $x) + ) + ) + (drop + (i64.and + (i64.const 1) + (local.get $y) + ) + ) + ;; No any bit overlapping, optimized + (drop + (i32.and + (i32.const 0x80000000) + (i32.const 0x7fffffff) + ) + ) + (drop + (i64.and + (i64.const 2) + (i64.const 1) + ) + ) + (drop + (i64.and + (i64.const 0x80000000) + (i64.const 0x7fffffff) + ) + ) + (drop + (i64.and + (i64.const 0x8000000000000000) + (i64.const 0x7fffffffffffffff) + ) + ) + ;; Just one bit overlapping, not optimized + (drop + (i32.and + (i32.const 0x80000001) + (i32.const 0x7fffffff) + ) + ) + (drop + (i64.and + (i64.const 0x80000001) + (i64.const 0x7fffffff) + ) + ) + ) ) From 0f09450a5557a3a033835aa567056c7e15cd1dcb Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 24 Apr 2025 21:15:52 +0800 Subject: [PATCH 13/31] Refactor --- src/passes/OptimizeInstructions.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 9a83adc76f9..3a4f45d9260 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -832,13 +832,11 @@ struct OptimizeInstructions return replaceCurrent(ret); } } - if (curr->op == AndInt32 || curr->op == AndInt64 || curr->op == OrInt32) { + if (curr->op == AndInt32 || curr->op == OrInt32) { if (curr->op == AndInt32) { if (auto* ret = combineAnd(curr)) { return replaceCurrent(ret); } - } - if (curr->op == AndInt32 || curr->op == AndInt64) { if (auto* ret = optimizeAndNoOverlappingBits(curr)) { return replaceCurrent(ret); } @@ -855,6 +853,12 @@ struct OptimizeInstructions return replaceCurrent(ret); } } + if (curr->op == AndInt64) { + if (auto* ret = optimizeAndNoOverlappingBits(curr)) { + return replaceCurrent(ret); + } + } + // relation/comparisons allow for math optimizations if (curr->isRelational()) { if (auto* ret = optimizeRelational(curr)) { From 37653fed684ca62a315a67afb12499054d28aea9 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu <91814026+xuruiyang2002@users.noreply.github.com> Date: Tue, 29 Apr 2025 09:23:13 +0800 Subject: [PATCH 14/31] Update src/passes/OptimizeInstructions.cpp Co-authored-by: Alon Zakai --- src/passes/OptimizeInstructions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 3a4f45d9260..3a235922f0e 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3569,7 +3569,7 @@ struct OptimizeInstructions auto* left = curr->left; auto* right = curr->right; - // Check right as constant and left's max bits + // Check right is constant and left's max bits auto leftMaxBits = Bits::getMaxBits(left, this); uint64_t maskLeft; if (leftMaxBits == 64) { From 0d8c6ad6dd98a872afc1938e77fb4eaed50b92c0 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 1 May 2025 20:13:50 +0800 Subject: [PATCH 15/31] Avoid redundant code --- src/passes/OptimizeInstructions.cpp | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 3a235922f0e..bd634a6f0f3 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3569,11 +3569,13 @@ struct OptimizeInstructions auto* left = curr->left; auto* right = curr->right; - // Check right is constant and left's max bits + // Check left's max bits and right is constant. auto leftMaxBits = Bits::getMaxBits(left, this); uint64_t maskLeft; if (leftMaxBits == 64) { - maskLeft = 0xffffffffffffffffULL; // All bits set for 64-bit case + // If we know nothing useful about the bits on the left, + // we can not optimize. + return nullptr; } else { maskLeft = (1ULL << leftMaxBits) - 1; } @@ -3581,23 +3583,7 @@ struct OptimizeInstructions uint64_t constantValue = c->value.getInteger(); if ((constantValue & maskLeft) == 0) { return getDroppedChildrenAndAppend( - curr, LiteralUtils::makeZero(curr->left->type, *getModule())); - } - } - - // Check left as constant and right's max bits - auto rightMaxBits = Bits::getMaxBits(right, this); - uint64_t maskRight; - if (rightMaxBits == 64) { - maskRight = 0xffffffffffffffffULL; // All bits set for 64-bit case - } else { - maskRight = (1ULL << rightMaxBits) - 1; - } - if (auto* c = left->dynCast()) { - uint64_t constantValue = c->value.getInteger(); - if ((constantValue & maskRight) == 0) { - return getDroppedChildrenAndAppend( - curr, LiteralUtils::makeZero(curr->right->type, *getModule())); + curr, LiteralUtils::makeZero(left->type, *getModule())); } } From f6de81a2cfe70902405dd3b35cc6362e58184a3f Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 1 May 2025 20:31:15 +0800 Subject: [PATCH 16/31] Update test --- .../lit/passes/optimize-instructions-mvp.wast | 111 +++++++++++++----- 1 file changed, 81 insertions(+), 30 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 1eb688d3732..d6549d015f2 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -1371,7 +1371,10 @@ ;; CHECK-NEXT: (i32.const 100) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 100) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $and-neg1 @@ -1390,7 +1393,10 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 100) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 1) @@ -1439,7 +1445,10 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.and @@ -17772,18 +17781,39 @@ ) ;; CHECK: (func $no-overlapping-bits-corner-case (param $x i32) (param $y i64) ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const -2147483648) + ;; CHECK-NEXT: (i32.const 2147483647) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (i64.const 2) ;; CHECK-NEXT: (i64.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (i64.const 2147483648) + ;; CHECK-NEXT: (i64.const 2147483647) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (i64.const -9223372036854775808) + ;; CHECK-NEXT: (i64.const 9223372036854775807) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.and ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (i32.const 1) @@ -17796,27 +17826,27 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.const -2147483647) ;; CHECK-NEXT: (i32.const 2147483647) + ;; CHECK-NEXT: (i32.const -2147483647) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (i64.const 2147483649) ;; CHECK-NEXT: (i64.const 2147483647) + ;; CHECK-NEXT: (i64.const 2147483649) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -17826,32 +17856,27 @@ ;; [0, n) always yields 0. Replace with zero. ;; Note: after swapping the operands, it also satisfies the commutative law. - ;; Unknown, not optimized - (drop - (i32.and - (local.get $x) - (i32.const 1) - ) - ) + ;; No any bit overlapping, optimized (drop (i64.and - (local.get $y) (i64.const 1) + (i64.const 2) ) ) (drop (i32.and - (i32.const 1) + (i32.const 0) (local.get $x) ) ) (drop (i64.and - (i64.const 1) + (i64.const 0) (local.get $y) ) ) - ;; No any bit overlapping, optimized + ;; optimizeAndNoOverlappingBits focuses on the case where one side is constant, + ;; both-constant cases are handled by Precompute, so they are skipped here. (drop (i32.and (i32.const 0x80000000) @@ -17870,23 +17895,49 @@ (i64.const 0x7fffffff) ) ) + ;; Know nothing useful about the bits on the left, so we can not optimize. (drop (i64.and (i64.const 0x8000000000000000) (i64.const 0x7fffffffffffffff) ) ) - ;; Just one bit overlapping, not optimized (drop (i32.and - (i32.const 0x80000001) + (local.get $x) + (i32.const 1) + ) + ) + (drop + (i64.and + (local.get $y) + (i64.const 1) + ) + ) + ;; Know nothing useful about the value of the right, so we can not optimize. + (drop + (i32.and + (i32.const 1) + (local.get $x) + ) + ) + (drop + (i64.and + (i64.const 1) + (local.get $y) + ) + ) + ;; One bit overlapped, not optimized + (drop + (i32.and (i32.const 0x7fffffff) + (i32.const 0x80000001) ) ) (drop (i64.and - (i64.const 0x80000001) (i64.const 0x7fffffff) + (i64.const 0x80000001) ) ) ) From 3f900fe6bdc1d8395b445ba344cea398150db3d8 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 1 May 2025 20:40:05 +0800 Subject: [PATCH 17/31] Adjust comments --- test/lit/passes/optimize-instructions-mvp.wast | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index d6549d015f2..f5f511c6242 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -17851,12 +17851,12 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $no-overlapping-bits-corner-case (param $x i32) (param $y i64) - ;; optimizeAndNoOverlappingBits will simplify AND operations where - ;; Bitwise AND of a value with bits in [0, n) and a constant with no bits in - ;; [0, n) always yields 0. Replace with zero. - ;; Note: after swapping the operands, it also satisfies the commutative law. + ;; optimizeAndNoOverlappingBits simplifies AND operations where + ;; - the left value covers bits in [0, n) + ;; - the right operand is a constant with no bits in [0, n) + ;; Result is simplified to zero. - ;; No any bit overlapping, optimized + ;; No any bit overlapped, optimized. (drop (i64.and (i64.const 1) @@ -17927,7 +17927,7 @@ (local.get $y) ) ) - ;; One bit overlapped, not optimized + ;; One bit overlapped, so we can not optimized. (drop (i32.and (i32.const 0x7fffffff) From 4dfe6ee0effa164cec0e10fe561cc564e44e9f42 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Thu, 1 May 2025 20:42:26 +0800 Subject: [PATCH 18/31] Adjust comments --- .../lit/passes/optimize-instructions-mvp.wast | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index f5f511c6242..70948638825 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -17814,12 +17814,6 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.and ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: (i64.const 1) @@ -17839,6 +17833,12 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.and ;; CHECK-NEXT: (i32.const 2147483647) ;; CHECK-NEXT: (i32.const -2147483647) ;; CHECK-NEXT: ) @@ -17902,12 +17902,6 @@ (i64.const 0x7fffffffffffffff) ) ) - (drop - (i32.and - (local.get $x) - (i32.const 1) - ) - ) (drop (i64.and (local.get $y) @@ -17928,6 +17922,12 @@ ) ) ;; One bit overlapped, so we can not optimized. + (drop + (i32.and + (local.get $x) + (i32.const 1) + ) + ) (drop (i32.and (i32.const 0x7fffffff) From e5bffdf6fbdd35a6a1e9bdc39f3c2fcb8f208111 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu <91814026+xuruiyang2002@users.noreply.github.com> Date: Fri, 2 May 2025 19:01:02 +0800 Subject: [PATCH 19/31] Update src/passes/OptimizeInstructions.cpp Co-authored-by: Alon Zakai --- src/passes/OptimizeInstructions.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index bd634a6f0f3..f85099e9d13 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3563,9 +3563,6 @@ struct OptimizeInstructions Expression* optimizeAndNoOverlappingBits(Binary* curr) { assert(curr->op == AndInt32 || curr->op == AndInt64); - using namespace Abstract; - using namespace Match; - auto* left = curr->left; auto* right = curr->right; From 4cc33e4828a21091e28c6e2d52617b259695ef48 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu <91814026+xuruiyang2002@users.noreply.github.com> Date: Fri, 2 May 2025 19:01:11 +0800 Subject: [PATCH 20/31] Update src/passes/OptimizeInstructions.cpp Co-authored-by: Alon Zakai --- src/passes/OptimizeInstructions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index f85099e9d13..e9d6aec40e0 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3569,7 +3569,7 @@ struct OptimizeInstructions // Check left's max bits and right is constant. auto leftMaxBits = Bits::getMaxBits(left, this); uint64_t maskLeft; - if (leftMaxBits == 64) { + if (leftMaxBits == left->type.getByteSize() * 8) { // If we know nothing useful about the bits on the left, // we can not optimize. return nullptr; From fb1411831407745db541139f42e2b9abc2d756e6 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu <91814026+xuruiyang2002@users.noreply.github.com> Date: Fri, 2 May 2025 19:01:18 +0800 Subject: [PATCH 21/31] Update src/passes/OptimizeInstructions.cpp Co-authored-by: Alon Zakai --- src/passes/OptimizeInstructions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index e9d6aec40e0..67c155ea4e3 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3571,7 +3571,7 @@ struct OptimizeInstructions uint64_t maskLeft; if (leftMaxBits == left->type.getByteSize() * 8) { // If we know nothing useful about the bits on the left, - // we can not optimize. + // we cannot optimize. return nullptr; } else { maskLeft = (1ULL << leftMaxBits) - 1; From 27608e9bd4faa57cd22e5fde88f750d7fa342a3e Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Fri, 2 May 2025 20:36:43 +0800 Subject: [PATCH 22/31] Update test --- .../lit/passes/optimize-instructions-mvp.wast | 133 ++++++++++-------- 1 file changed, 77 insertions(+), 56 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 70948638825..8e65facb23b 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -17779,7 +17779,18 @@ (i32.const 1) ) ) - ;; CHECK: (func $no-overlapping-bits-corner-case (param $x i32) (param $y i64) + ;; CHECK: (func $no-overlapping-bits-corner-case (param $0 i32) (param $1 i64) + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (local $y i64) + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.set $y + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) @@ -17790,154 +17801,164 @@ ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.const -2147483648) ;; CHECK-NEXT: (i32.const 2147483647) + ;; CHECK-NEXT: (i32.const -2147483647) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (i64.const 2) - ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: (i64.const 2147483647) + ;; CHECK-NEXT: (i64.const 2147483649) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (i64.const 2147483648) - ;; CHECK-NEXT: (i64.const 2147483647) + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (i64.const -9223372036854775808) - ;; CHECK-NEXT: (i64.const 9223372036854775807) + ;; CHECK-NEXT: (i64.const 2) + ;; CHECK-NEXT: (i64.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: (i64.const 2147483648) + ;; CHECK-NEXT: (i64.const 2147483647) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (i32.const -2147483648) + ;; CHECK-NEXT: (i32.const 2147483647) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: (i64.const -9223372036854775808) + ;; CHECK-NEXT: (i64.const 9223372036854775807) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.const 2147483647) - ;; CHECK-NEXT: (i32.const -2147483647) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (i64.const 2147483647) - ;; CHECK-NEXT: (i64.const 2147483649) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (i64.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $no-overlapping-bits-corner-case (param $x i32) (param $y i64) + (func $no-overlapping-bits-corner-case (param $0 i32) (param $1 i64) ;; optimizeAndNoOverlappingBits simplifies AND operations where ;; - the left value covers bits in [0, n) ;; - the right operand is a constant with no bits in [0, n) ;; Result is simplified to zero. - - ;; No any bit overlapped, optimized. - (drop - (i64.and - (i64.const 1) - (i64.const 2) - ) + (local $x i32) + (local $y i64) + (local.set $x + (i32.const 1) + ) + (local.set $y + (i64.const 1) ) + ;; No any bit overlapped, optimized. (drop (i32.and - (i32.const 0) (local.get $x) + (i32.const 2) ) ) (drop (i64.and - (i64.const 0) (local.get $y) + (i64.const 2) ) ) - ;; optimizeAndNoOverlappingBits focuses on the case where one side is constant, - ;; both-constant cases are handled by Precompute, so they are skipped here. + ;; Both-constant cases which meets the condition are also optimized. (drop (i32.and - (i32.const 0x80000000) - (i32.const 0x7fffffff) + (i32.const 1) + (i32.const 2) ) ) (drop (i64.and - (i64.const 2) (i64.const 1) + (i64.const 2) ) ) (drop (i64.and - (i64.const 0x80000000) (i64.const 0x7fffffff) + (i64.const 0x80000000) ) ) - ;; Know nothing useful about the bits on the left, so we can not optimize. + ;; One bit overlapped, so we can not optimized. (drop - (i64.and - (i64.const 0x8000000000000000) - (i64.const 0x7fffffffffffffff) + (i32.and + (i32.const 0x7fffffff) + (i32.const 0x80000001) ) ) (drop (i64.and - (local.get $y) - (i64.const 1) + (i64.const 0x7fffffff) + (i64.const 0x80000001) ) ) - ;; Know nothing useful about the value of the right, so we can not optimize. + ;; Both-constant cases which does not meets the condition are handled + ;; by Precompute, so they are skipped here. (drop (i32.and + (i32.const 2) (i32.const 1) - (local.get $x) ) ) (drop (i64.and + (i64.const 2) (i64.const 1) - (local.get $y) ) ) - ;; One bit overlapped, so we can not optimized. (drop - (i32.and - (local.get $x) - (i32.const 1) + (i64.and + (i64.const 0x80000000) + (i64.const 0x7fffffff) ) ) + ;; Know nothing useful about the bits on the left, so we can not optimize. (drop (i32.and + (i32.const 0x80000000) (i32.const 0x7fffffff) - (i32.const 0x80000001) ) ) (drop (i64.and - (i64.const 0x7fffffff) - (i64.const 0x80000001) + (i64.const 0x8000000000000000) + (i64.const 0x7fffffffffffffff) + ) + ) + (drop + (i32.and + (local.get $0) + (i32.const 1) + ) + ) + (drop + (i64.and + (local.get $1) + (i64.const 1) ) ) ) From 4ae987c9853e2b3f54e5240ea8cc04682cef7536 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Fri, 16 May 2025 09:26:57 +0800 Subject: [PATCH 23/31] Concrete type --- src/passes/OptimizeInstructions.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 67c155ea4e3..587be5affd3 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3569,7 +3569,8 @@ struct OptimizeInstructions // Check left's max bits and right is constant. auto leftMaxBits = Bits::getMaxBits(left, this); uint64_t maskLeft; - if (leftMaxBits == left->type.getByteSize() * 8) { + if (left->type.isConcrete() && + leftMaxBits == left->type.getByteSize() * 8) { // If we know nothing useful about the bits on the left, // we cannot optimize. return nullptr; From 4eadb01fd4fe16c2559c02cd3d9d8c18b9be8a5c Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Fri, 16 May 2025 09:30:52 +0800 Subject: [PATCH 24/31] Update --- src/passes/OptimizeInstructions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 587be5affd3..17ac33e8ae0 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3569,7 +3569,7 @@ struct OptimizeInstructions // Check left's max bits and right is constant. auto leftMaxBits = Bits::getMaxBits(left, this); uint64_t maskLeft; - if (left->type.isConcrete() && + if (!left->type.isConcrete() || leftMaxBits == left->type.getByteSize() * 8) { // If we know nothing useful about the bits on the left, // we cannot optimize. From 7b7a4207d19fad1453629073774ee51a7536739f Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Fri, 16 May 2025 09:41:16 +0800 Subject: [PATCH 25/31] Use isNumber instead of isConcrete --- src/passes/OptimizeInstructions.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 17ac33e8ae0..e22e7b223be 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3569,8 +3569,7 @@ struct OptimizeInstructions // Check left's max bits and right is constant. auto leftMaxBits = Bits::getMaxBits(left, this); uint64_t maskLeft; - if (!left->type.isConcrete() || - leftMaxBits == left->type.getByteSize() * 8) { + if (!left->type.isNumber() || leftMaxBits == left->type.getByteSize() * 8) { // If we know nothing useful about the bits on the left, // we cannot optimize. return nullptr; From a612db0b115d003bc586cc4b6c41dccf7099e037 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Sat, 24 May 2025 00:22:23 +0800 Subject: [PATCH 26/31] Simplify tests --- .../lit/passes/optimize-instructions-mvp.wast | 159 ++++++++---------- 1 file changed, 68 insertions(+), 91 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 8e65facb23b..4b1e8152995 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -17779,15 +17779,7 @@ (i32.const 1) ) ) - ;; CHECK: (func $no-overlapping-bits-corner-case (param $0 i32) (param $1 i64) - ;; CHECK-NEXT: (local $x i32) - ;; CHECK-NEXT: (local $y i64) - ;; CHECK-NEXT: (local.set $x - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (local.set $y - ;; CHECK-NEXT: (i64.const 1) - ;; CHECK-NEXT: ) + ;; CHECK: (func $add-op-no-overlapping-bits-corner-case ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -17795,96 +17787,15 @@ ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.const 2147483647) - ;; CHECK-NEXT: (i32.const -2147483647) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (i64.const 2147483647) - ;; CHECK-NEXT: (i64.const 2147483649) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.const 2) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (i64.const 2) - ;; CHECK-NEXT: (i64.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (i64.const 2147483648) - ;; CHECK-NEXT: (i64.const 2147483647) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.const -2147483648) - ;; CHECK-NEXT: (i32.const 2147483647) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (i64.const -9223372036854775808) - ;; CHECK-NEXT: (i64.const 9223372036854775807) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (local.get $0) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.and - ;; CHECK-NEXT: (local.get $1) - ;; CHECK-NEXT: (i64.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $no-overlapping-bits-corner-case (param $0 i32) (param $1 i64) + (func $add-op-no-overlapping-bits-corner-case ;; optimizeAndNoOverlappingBits simplifies AND operations where ;; - the left value covers bits in [0, n) ;; - the right operand is a constant with no bits in [0, n) ;; Result is simplified to zero. - (local $x i32) - (local $y i64) - (local.set $x - (i32.const 1) - ) - (local.set $y - (i64.const 1) - ) ;; No any bit overlapped, optimized. - (drop - (i32.and - (local.get $x) - (i32.const 2) - ) - ) - (drop - (i64.and - (local.get $y) - (i64.const 2) - ) - ) - ;; Both-constant cases which meets the condition are also optimized. (drop (i32.and (i32.const 1) @@ -17903,6 +17814,22 @@ (i64.const 0x80000000) ) ) + ) + ;; CHECK: (func $add-op-overlapping-bits-corner-case + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 2147483647) + ;; CHECK-NEXT: (i32.const -2147483647) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (i64.const 2147483647) + ;; CHECK-NEXT: (i64.const 2147483649) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $add-op-overlapping-bits-corner-case ;; One bit overlapped, so we can not optimized. (drop (i32.and @@ -17916,6 +17843,28 @@ (i64.const 0x80000001) ) ) + ) + ;; CHECK: (func $add-op-no-overlapping-skipped + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (i64.const 2) + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (i64.const 2147483648) + ;; CHECK-NEXT: (i64.const 2147483647) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $add-op-no-overlapping-skipped ;; Both-constant cases which does not meets the condition are handled ;; by Precompute, so they are skipped here. (drop @@ -17936,6 +17885,34 @@ (i64.const 0x7fffffff) ) ) + ) + ;; CHECK: (func $add-op-unknown-useful (param $0 i32) (param $1 i64) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (i32.const -2147483648) + ;; CHECK-NEXT: (i32.const 2147483647) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (i64.const -9223372036854775808) + ;; CHECK-NEXT: (i64.const 9223372036854775807) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.and + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $add-op-unknown-useful (param $0 i32) (param $1 i64) ;; Know nothing useful about the bits on the left, so we can not optimize. (drop (i32.and From 8f663bb5a24f2e106c57b5d8b3254caa6a9c77c9 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu <91814026+xuruiyang2002@users.noreply.github.com> Date: Sat, 24 May 2025 00:56:01 +0800 Subject: [PATCH 27/31] Update test/lit/passes/optimize-instructions-mvp.wast Co-authored-by: Alon Zakai --- test/lit/passes/optimize-instructions-mvp.wast | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 4b1e8152995..63d23f9d38c 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -17795,7 +17795,7 @@ ;; - the left value covers bits in [0, n) ;; - the right operand is a constant with no bits in [0, n) ;; Result is simplified to zero. - ;; No any bit overlapped, optimized. + ;; No bit overlaps, so we optimize. (drop (i32.and (i32.const 1) From b57c3697c921e1a59bd4fded73dbf0491c86742e Mon Sep 17 00:00:00 2001 From: Ruiyang Xu <91814026+xuruiyang2002@users.noreply.github.com> Date: Sat, 24 May 2025 00:56:12 +0800 Subject: [PATCH 28/31] Update test/lit/passes/optimize-instructions-mvp.wast Co-authored-by: Alon Zakai --- test/lit/passes/optimize-instructions-mvp.wast | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 63d23f9d38c..4a5eab5d560 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -17830,7 +17830,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $add-op-overlapping-bits-corner-case - ;; One bit overlapped, so we can not optimized. + ;; One bit overlaps, so we cannot optimize. (drop (i32.and (i32.const 0x7fffffff) From d1c0cf650f807d45f3cb7562d66467ddf75c24b6 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu <91814026+xuruiyang2002@users.noreply.github.com> Date: Sat, 24 May 2025 00:56:55 +0800 Subject: [PATCH 29/31] Update test/lit/passes/optimize-instructions-mvp.wast Co-authored-by: Alon Zakai --- test/lit/passes/optimize-instructions-mvp.wast | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 4a5eab5d560..0b1b113bafe 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -17865,8 +17865,8 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $add-op-no-overlapping-skipped - ;; Both-constant cases which does not meets the condition are handled - ;; by Precompute, so they are skipped here. + ;; Both-constant cases which do not meet the condition (mask of left has no + ;; overlap with right) is left for Precompute. (drop (i32.and (i32.const 2) From a3f26d9b2f2132c3f239db3156622ab544bc9546 Mon Sep 17 00:00:00 2001 From: Ruiyang Xu <91814026+xuruiyang2002@users.noreply.github.com> Date: Sat, 24 May 2025 00:57:03 +0800 Subject: [PATCH 30/31] Update test/lit/passes/optimize-instructions-mvp.wast Co-authored-by: Alon Zakai --- test/lit/passes/optimize-instructions-mvp.wast | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 0b1b113bafe..99979a7b342 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -17913,7 +17913,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $add-op-unknown-useful (param $0 i32) (param $1 i64) - ;; Know nothing useful about the bits on the left, so we can not optimize. + ;; We know nothing useful about the bits on the left, so we cannot optimize. (drop (i32.and (i32.const 0x80000000) From 440b963b43da12873bafef599ac28a7aaebcd9ac Mon Sep 17 00:00:00 2001 From: Ruiyang Xu Date: Sat, 24 May 2025 01:24:40 +0800 Subject: [PATCH 31/31] Update test --- .../lit/passes/optimize-instructions-mvp.wast | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 99979a7b342..77fc0121a28 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -17779,7 +17779,7 @@ (i32.const 1) ) ) - ;; CHECK: (func $add-op-no-overlapping-bits-corner-case + ;; CHECK: (func $add-op-no-overlapping-bits-corner-case (param $0 i32) (param $1 i64) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -17789,8 +17789,14 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i64.const 0) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $add-op-no-overlapping-bits-corner-case + (func $add-op-no-overlapping-bits-corner-case (param $0 i32) (param $1 i64) ;; optimizeAndNoOverlappingBits simplifies AND operations where ;; - the left value covers bits in [0, n) ;; - the right operand is a constant with no bits in [0, n) @@ -17814,6 +17820,26 @@ (i64.const 0x80000000) ) ) + ;; We know something (but not constant) about the bits + ;; on the left, so we can optimize. + (drop + (i32.and + (i32.and + (local.get $0) + (i32.const 0xff) + ) + (i32.const 0xff00) + ) + ) + (drop + (i64.and + (i64.and + (local.get $1) + (i64.const 0xff) + ) + (i64.const 0xff00) + ) + ) ) ;; CHECK: (func $add-op-overlapping-bits-corner-case ;; CHECK-NEXT: (drop