From e3a4e7df7a649b2e4114adb990d033b6165915f2 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 09:44:14 -0700 Subject: [PATCH 01/22] start --- src/passes/OptimizeInstructions.cpp | 86 ++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 15 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 74bfca899ea..462b4a425b5 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -51,6 +51,20 @@ namespace wasm { +static Index getBitsForType(Type type) { + if (!type.isBasic()) { + return -1; + } + switch (type.getBasic()) { + case Type::i32: + return 32; + case Type::i64: + return 64; + default: + return -1; + } +} + // Useful information about locals struct LocalInfo { static const Index kUnknown = Index(-1); @@ -123,20 +137,6 @@ struct LocalScanner : PostWalker { // define this for the templated getMaxBits method. we know nothing here yet // about locals, so return the maxes Index getMaxBitsForLocal(LocalGet* get) { return getBitsForType(get->type); } - - Index getBitsForType(Type type) { - if (!type.isBasic()) { - return -1; - } - switch (type.getBasic()) { - case Type::i32: - return 32; - case Type::i64: - return 64; - default: - return -1; - } - } }; namespace { @@ -3382,7 +3382,7 @@ struct OptimizeInstructions binary(DivSInt64, any(), i64(std::numeric_limits::min())))) { curr->op = EqInt64; curr->type = Type::i32; - return Builder(*getModule()).makeUnary(ExtendUInt32, curr); + return builder.makeUnary(ExtendUInt32, curr); } // (unsigned)x < 0 ==> i32(0) if (matches(curr, binary(LtU, pure(&left), ival(0)))) { @@ -3567,9 +3567,65 @@ struct OptimizeInstructions return left; } } + { + // (unsigned) x + C1 > C2 ==> x > C2-C1 if no overflowing + // (unsigned) x + C1 >= C2 ==> x >= C2-C1 if no overflowing + Binary* add; + Const* c1; + Const* c2; + if ((matches(curr, binary(GtU, binary(&add, Add, any(), ival(&c1)), ival(&c2))) || + matches(curr, binary(GeU, binary(&add, Add, any(), ival(&c1)), ival(&c2)))) + !canOverflow(add)) { + if (c1->value.gtU(c2->value)) { + // C2-C1 overflows. This is a situation that looks like this: + // (unsigned) x + 10 > 5 + // The result is always true. + c1->value = Literal(int32_t(1)); + c1->type = Type::i32; + return builder.makeSequence(makeDrop(add->left), c1); + } + c2->value = c2->value.sub(c1->value); + curr->left = add->left; + return curr; + } + } return nullptr; } + // Returns true if the given binary operation can overflow. If we can't be + // sure either way, we return true, assuming the worst. + bool canOverflow(Binary* binary) { + using namespace Abstract; + + // If we know nothing about a limit on the amount of bits on either side, + // give up. + auto typeMaxBits = getBitsForType(binary->type); + auto leftMaxBits = Bits::getMaxBits(binary->left, this); + if (leftMaxBits == typeMaxBits) { + return true; + } + auto rightMaxBits = Bits::getMaxBits(binary->right, this); + if (rightMaxBits == typeMaxBits) { + return true; + } + + if (binary->op == getBinary(binary->type, Add)) { + // left < 2^(leftMaxBits-1) + // right < 2^(rightMaxBits-1) + // => + // left + right <= + , and right is at most + // 2^(rightMaxBits-1) - 1, so + // When adding or subtracting, we can get an overflow of one extra bit. + // E.g. if left has 30 bits and right has 1 bit, we might have + // 0x3fffffff + 0x1 + return leftMaxBits + rightMaxBits + 1 < typeMaxBits; + } + + // TODO subtraction etc. + return true; + } + // Folding two expressions into one with similar operations and // constants on RHSs Expression* optimizeDoubletonWithConstantOnRight(Binary* curr) { From 740b0975507372f63fd6a56424d35d77acaf8531 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 09:54:11 -0700 Subject: [PATCH 02/22] work --- src/passes/OptimizeInstructions.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 462b4a425b5..14e46d61262 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3610,16 +3610,17 @@ struct OptimizeInstructions } if (binary->op == getBinary(binary->type, Add)) { - // left < 2^(leftMaxBits-1) - // right < 2^(rightMaxBits-1) - // => - // left + right <= - , and right is at most - // 2^(rightMaxBits-1) - 1, so - // When adding or subtracting, we can get an overflow of one extra bit. - // E.g. if left has 30 bits and right has 1 bit, we might have - // 0x3fffffff + 0x1 - return leftMaxBits + rightMaxBits + 1 < typeMaxBits; + // Proof this cannot overflow: + // + // left + right < 2^leftMaxBits + 2^rightMaxBits (1) + // <= 2^(typeMaxBits-1) + 2^(typeMaxBits-1) (2) + // = 2^typeMaxBits (3) + // + // (1) By the definition of the max bits (e.g. an int32 has 32 max bits, + // and its max value is 2^32 - 1, which is < 2^32). + // (2) By the above checks and early returns. + // (3) 2^x + 2^x === 2*2^x === 2^(x+1) + return false; } // TODO subtraction etc. From e5c41655eae4bf0f98feee2b58c47562abe458d1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 09:54:33 -0700 Subject: [PATCH 03/22] format --- 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 14e46d61262..43c5bf1cfb1 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3573,9 +3573,13 @@ struct OptimizeInstructions Binary* add; Const* c1; Const* c2; - if ((matches(curr, binary(GtU, binary(&add, Add, any(), ival(&c1)), ival(&c2))) || - matches(curr, binary(GeU, binary(&add, Add, any(), ival(&c1)), ival(&c2)))) - !canOverflow(add)) { + if ((matches( + curr, + binary(GtU, binary(&add, Add, any(), ival(&c1)), ival(&c2))) || + matches(curr, + binary(GeU, + binary(&add, Add, any(), ival(&c1)), + ival(&c2)))) !canOverflow(add)) { if (c1->value.gtU(c2->value)) { // C2-C1 overflows. This is a situation that looks like this: // (unsigned) x + 10 > 5 From 3ec0f21717d47d08d3c711520b828c1343430bc0 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 09:55:51 -0700 Subject: [PATCH 04/22] builds --- src/passes/OptimizeInstructions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 43c5bf1cfb1..e52fdf13d15 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3579,14 +3579,14 @@ struct OptimizeInstructions matches(curr, binary(GeU, binary(&add, Add, any(), ival(&c1)), - ival(&c2)))) !canOverflow(add)) { - if (c1->value.gtU(c2->value)) { + ival(&c2)))) && !canOverflow(add)) { + if (c1->value.gtU(c2->value).getInteger()) { // C2-C1 overflows. This is a situation that looks like this: // (unsigned) x + 10 > 5 // The result is always true. c1->value = Literal(int32_t(1)); c1->type = Type::i32; - return builder.makeSequence(makeDrop(add->left), c1); + return builder.makeSequence(builder.makeDrop(add->left), c1); } c2->value = c2->value.sub(c1->value); curr->left = add->left; From 7a496790e7e79b73df0e688b94262cf1ff47611f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 09:55:56 -0700 Subject: [PATCH 05/22] format --- src/passes/OptimizeInstructions.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index e52fdf13d15..7f4a5bd14c6 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3576,10 +3576,10 @@ struct OptimizeInstructions if ((matches( curr, binary(GtU, binary(&add, Add, any(), ival(&c1)), ival(&c2))) || - matches(curr, - binary(GeU, - binary(&add, Add, any(), ival(&c1)), - ival(&c2)))) && !canOverflow(add)) { + matches( + curr, + binary(GeU, binary(&add, Add, any(), ival(&c1)), ival(&c2)))) && + !canOverflow(add)) { if (c1->value.gtU(c2->value).getInteger()) { // C2-C1 overflows. This is a situation that looks like this: // (unsigned) x + 10 > 5 From f56628874850749895fd5ed040d735d87b99e34d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 10:07:53 -0700 Subject: [PATCH 06/22] test --- test/lit/passes/optimize-instructions.wast | 126 +++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index d8c72bb65ef..cc22fa4bb1b 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -15111,4 +15111,130 @@ ) ) ) + + ;; CHECK: (func $gt_u-added-constant (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.gt_u + ;; CHECK-NEXT: (i32.shr_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 5) + ;; 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 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.shr_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gt_u-added-constant (param $x i32) + ;; A + 5 > 10 => A > 5, since this cannot overflow (A and the constants + ;; are small enough) + (drop + (i32.gt_u + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 5) + ) + (i32.const 10) + ) + ) + ;; We can optimize even if the constants are equal. + (drop + (i32.gt_u + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 5) + ) + (i32.const 5) + ) + ) + ;; If the first constant is larger, the result is trivial: this must be + ;; true. + (drop + (i32.gt_u + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 6) + ) + (i32.const 5) + ) + ) + ) + + ;; CHECK: (func $gt_u-added-constant-no (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.gt_u + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.gt_u + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (i32.shr_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -2147483648) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $gt_u-added-constant-no (param $x i32) + ;; As above, but without the shr_u, A is big enough for a possible overflow, + ;; and we cannot optimize. + (drop + (i32.gt_u + (i32.add + (local.get $x) + (i32.const 5) + ) + (i32.const 10) + ) + ) + ;; With the added constant too big, it might overflow, and we cannot + ;; optimize. + (drop + (i32.gt_u + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 0x80000000) + ) + (i32.const 10) + ) + ) + ) ) From 7617d86e3f8fbf7c7bb6dcf73dae7d745f9689d4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 10:09:13 -0700 Subject: [PATCH 07/22] test --- test/lit/passes/optimize-instructions.wast | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index cc22fa4bb1b..a98163ea97b 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -15209,6 +15209,18 @@ ;; CHECK-NEXT: (i32.const 10) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.gt_u + ;; CHECK-NEXT: (i32.sub + ;; 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 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $gt_u-added-constant-no (param $x i32) ;; As above, but without the shr_u, A is big enough for a possible overflow, @@ -15236,5 +15248,17 @@ (i32.const 10) ) ) + (drop + (i32.gt_u + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 0xffffffff) + ) + (i32.const 10) + ) + ) ) ) From ac9b7a307a4a821c26d817238b2be6b08631c462 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 10:10:22 -0700 Subject: [PATCH 08/22] test --- test/lit/passes/optimize-instructions.wast | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index a98163ea97b..79b13bf5426 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -15261,4 +15261,31 @@ ) ) ) + + ;; CHECK: (func $ge_u-added-constant (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.ge_u + ;; CHECK-NEXT: (i32.shr_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ge_u-added-constant (param $x i32) + ;; As above, but with ge rather than gt. We can optimize here. + (drop + (i32.ge_u + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 5) + ) + (i32.const 10) + ) + ) + ) ) From 96169a9b8ed5f829826d30528db29d1266bec5ff Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 10:12:34 -0700 Subject: [PATCH 09/22] test --- test/lit/passes/optimize-instructions.wast | 110 +++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index 79b13bf5426..2d70f3b5f67 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -15272,6 +15272,20 @@ ;; CHECK-NEXT: (i32.const 5) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.shr_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $ge_u-added-constant (param $x i32) ;; As above, but with ge rather than gt. We can optimize here. @@ -15287,5 +15301,101 @@ (i32.const 10) ) ) + (drop + (i32.ge_u + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 5) + ) + (i32.const 5) + ) + ) + (drop + (i32.ge_u + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 6) + ) + (i32.const 5) + ) + ) + ) + + ;; CHECK: (func $ge_u-added-constant-no (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.ge_u + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 5) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.ge_u + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (i32.shr_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -2147483648) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.ge_u + ;; CHECK-NEXT: (i32.sub + ;; 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 10) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ge_u-added-constant-no (param $x i32) + ;; As above, but with ge rather than gt. We cannot optimize here. + (drop + (i32.ge_u + (i32.add + (local.get $x) + (i32.const 5) + ) + (i32.const 10) + ) + ) + (drop + (i32.ge_u + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 0x80000000) + ) + (i32.const 10) + ) + ) + (drop + (i32.ge_u + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 0xffffffff) + ) + (i32.const 10) + ) + ) ) ) From f90dad28b9075c4884a35bbd40bb775660339ed9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 10:15:45 -0700 Subject: [PATCH 10/22] refactor --- src/passes/OptimizeInstructions.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 7f4a5bd14c6..958285dff48 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1474,6 +1474,13 @@ struct OptimizeInstructions } } + // Appends a result after the dropped children, if we need them. + Expression* getDroppedChildrenAndAppend(Expression* curr, + Expression* result) { + return wasm::getDroppedChildrenAndAppend( + curr, *getModule(), getPassOptions(), result); + } + void visitRefEq(RefEq* curr) { // The types may prove that the same reference cannot appear on both sides. auto leftType = curr->left->type; @@ -1495,7 +1502,7 @@ struct OptimizeInstructions auto* result = Builder(*getModule()).makeConst(Literal::makeZero(Type::i32)); replaceCurrent(getDroppedChildrenAndAppend( - curr, *getModule(), getPassOptions(), result)); + curr, result)); return; } @@ -1517,7 +1524,7 @@ struct OptimizeInstructions auto* result = Builder(*getModule()).makeConst(Literal::makeOne(Type::i32)); replaceCurrent(getDroppedChildrenAndAppend( - curr, *getModule(), getPassOptions(), result)); + curr, result)); return; } From d6e0fc039c8e7a36f5e9e39a8eb2b78b9809c74a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 10:15:50 -0700 Subject: [PATCH 11/22] nicer --- src/passes/OptimizeInstructions.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 958285dff48..1d49e4b4621 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1501,8 +1501,7 @@ struct OptimizeInstructions // reference can appear on both sides. auto* result = Builder(*getModule()).makeConst(Literal::makeZero(Type::i32)); - replaceCurrent(getDroppedChildrenAndAppend( - curr, result)); + replaceCurrent(getDroppedChildrenAndAppend(curr, result)); return; } @@ -1523,8 +1522,7 @@ struct OptimizeInstructions if (areConsecutiveInputsEqualAndFoldable(curr->left, curr->right)) { auto* result = Builder(*getModule()).makeConst(Literal::makeOne(Type::i32)); - replaceCurrent(getDroppedChildrenAndAppend( - curr, result)); + replaceCurrent(getDroppedChildrenAndAppend(curr, result)); return; } From 4ba6e912da8938532394f81f7b7e045032bf6964 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 10:16:41 -0700 Subject: [PATCH 12/22] better --- 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 1d49e4b4621..ab81421f848 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3591,7 +3591,7 @@ struct OptimizeInstructions // The result is always true. c1->value = Literal(int32_t(1)); c1->type = Type::i32; - return builder.makeSequence(builder.makeDrop(add->left), c1); + return getDroppedChildrenAndAppend(curr, c1); } c2->value = c2->value.sub(c1->value); curr->left = add->left; From e6041d0e36f4e1761323e7dc0d35b98ba5f8fa1f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 10:17:16 -0700 Subject: [PATCH 13/22] better --- test/lit/passes/optimize-instructions.wast | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index 2d70f3b5f67..78412c4cd7f 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -15132,15 +15132,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.shr_u - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; 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) @@ -15276,15 +15268,7 @@ ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block (result i32) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.shr_u - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $ge_u-added-constant (param $x i32) From 0ba42dca166ea9358a10c1ab5e033b7fc9ef29ea Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 10:18:00 -0700 Subject: [PATCH 14/22] todo --- src/passes/OptimizeInstructions.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index ab81421f848..97bb09448ca 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -495,6 +495,8 @@ struct OptimizeInstructions } { // unsigned(x) >= 0 => i32(1) + // TODO: Use getDroppedChildrenAndAppend() here, so we can optimize even + // if pure. Const* c; Expression* x; if (matches(curr, binary(GeU, pure(&x), ival(&c))) && From 63e4b6b3f9f824bd2c4212ef8ca4a77eb4d82e71 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 7 Sep 2022 15:04:32 -0700 Subject: [PATCH 15/22] test --- test/lit/passes/optimize-instructions.wast | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index 78412c4cd7f..882b2f96df3 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -15119,7 +15119,7 @@ ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 5) + ;; CHECK-NEXT: (i32.const 6) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop @@ -15147,7 +15147,7 @@ ) (i32.const 5) ) - (i32.const 10) + (i32.const 11) ) ) ;; We can optimize even if the constants are equal. @@ -15186,7 +15186,7 @@ ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (i32.const 5) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 11) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop @@ -15198,7 +15198,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const -2147483648) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 11) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop @@ -15210,7 +15210,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 11) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -15223,7 +15223,7 @@ (local.get $x) (i32.const 5) ) - (i32.const 10) + (i32.const 11) ) ) ;; With the added constant too big, it might overflow, and we cannot @@ -15237,7 +15237,7 @@ ) (i32.const 0x80000000) ) - (i32.const 10) + (i32.const 11) ) ) (drop @@ -15249,7 +15249,7 @@ ) (i32.const 0xffffffff) ) - (i32.const 10) + (i32.const 11) ) ) ) @@ -15261,7 +15261,7 @@ ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 5) + ;; CHECK-NEXT: (i32.const 6) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop @@ -15282,7 +15282,7 @@ ) (i32.const 5) ) - (i32.const 10) + (i32.const 11) ) ) (drop @@ -15318,7 +15318,7 @@ ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: (i32.const 5) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 11) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop @@ -15330,7 +15330,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const -2147483648) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 11) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop @@ -15342,7 +15342,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 10) + ;; CHECK-NEXT: (i32.const 11) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -15354,7 +15354,7 @@ (local.get $x) (i32.const 5) ) - (i32.const 10) + (i32.const 11) ) ) (drop @@ -15366,7 +15366,7 @@ ) (i32.const 0x80000000) ) - (i32.const 10) + (i32.const 11) ) ) (drop @@ -15378,7 +15378,7 @@ ) (i32.const 0xffffffff) ) - (i32.const 10) + (i32.const 11) ) ) ) From 6621ac650298e9d184d6feaa14a795c317390f63 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 8 Sep 2022 07:57:54 -0700 Subject: [PATCH 16/22] shorter --- src/passes/OptimizeInstructions.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 97bb09448ca..029141cf8de 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3612,11 +3612,8 @@ struct OptimizeInstructions // give up. auto typeMaxBits = getBitsForType(binary->type); auto leftMaxBits = Bits::getMaxBits(binary->left, this); - if (leftMaxBits == typeMaxBits) { - return true; - } auto rightMaxBits = Bits::getMaxBits(binary->right, this); - if (rightMaxBits == typeMaxBits) { + if (std::max(leftMaxBits, rightMaxBits) == typeMaxBits) { return true; } From 7ddf48bfb4fbed58a097e2218aba6ccbc85bb0d9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 8 Sep 2022 08:07:00 -0700 Subject: [PATCH 17/22] better? --- src/passes/OptimizeInstructions.cpp | 36 +++++++++++++--------- test/lit/passes/optimize-instructions.wast | 8 ++++- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 029141cf8de..ceb4961ca14 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3574,30 +3574,36 @@ struct OptimizeInstructions return left; } } - { - // (unsigned) x + C1 > C2 ==> x > C2-C1 if no overflowing - // (unsigned) x + C1 >= C2 ==> x >= C2-C1 if no overflowing + // x + C1 > C2 ==> x > (C2-C1) if no overflowing, C2 >= C1 + // x + C1 > C2 ==> x + (C1-C2) > 0 if no overflowing, C2 < C1 + // And similarly for other relational operations. + if (curr->isRelational()) { Binary* add; Const* c1; Const* c2; if ((matches( curr, - binary(GtU, binary(&add, Add, any(), ival(&c1)), ival(&c2))) || + binary(binary(&add, Add, any(), ival(&c1)), ival(&c2))) || matches( curr, - binary(GeU, binary(&add, Add, any(), ival(&c1)), ival(&c2)))) && + binary(binary(&add, Add, any(), ival(&c1)), ival(&c2)))) && !canOverflow(add)) { - if (c1->value.gtU(c2->value).getInteger()) { - // C2-C1 overflows. This is a situation that looks like this: - // (unsigned) x + 10 > 5 - // The result is always true. - c1->value = Literal(int32_t(1)); - c1->type = Type::i32; - return getDroppedChildrenAndAppend(curr, c1); + if (c2->value.geU(c1->value).getInteger()) { + // This is the first line above, we turn into x > (C2-C1) + c2->value = c2->value.sub(c1->value); + curr->left = add->left; + return curr; + } + // This is the second line above, we turn into x + (C1-C2) > 0. Other + // optimizations can often kick in later. However, we must rule out the + // case where C2 is already 0 (to avoid continuing to think we are + // improving forever). + auto zero = Literal::makeZero(c2->type); + if (c2->value != zero) { + c1->value = c1->value.sub(c2->value); + c2->value = zero; + return curr; } - c2->value = c2->value.sub(c1->value); - curr->left = add->left; - return curr; } } return nullptr; diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index 882b2f96df3..d3de5a54e93 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -15132,7 +15132,13 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.const 1) + ;; 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: ) ;; CHECK-NEXT: ) (func $gt_u-added-constant (param $x i32) From 643df0cad03451136b94990c5ab8bee4c549df19 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 8 Sep 2022 08:07:10 -0700 Subject: [PATCH 18/22] format --- src/passes/OptimizeInstructions.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index ceb4961ca14..86a89c4e4e3 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3581,12 +3581,10 @@ struct OptimizeInstructions Binary* add; Const* c1; Const* c2; - if ((matches( - curr, - binary(binary(&add, Add, any(), ival(&c1)), ival(&c2))) || - matches( - curr, - binary(binary(&add, Add, any(), ival(&c1)), ival(&c2)))) && + if ((matches(curr, + binary(binary(&add, Add, any(), ival(&c1)), ival(&c2))) || + matches(curr, + binary(binary(&add, Add, any(), ival(&c1)), ival(&c2)))) && !canOverflow(add)) { if (c2->value.geU(c1->value).getInteger()) { // This is the first line above, we turn into x > (C2-C1) From 3ca8ec0671402239be803bf45602f050c8b72cb1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 8 Sep 2022 15:12:48 -0700 Subject: [PATCH 19/22] feedback --- src/passes/OptimizeInstructions.cpp | 11 ++--------- test/lit/passes/optimize-instructions.wast | 7 +++---- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 86a89c4e4e3..a483fef6527 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -52,17 +52,10 @@ namespace wasm { static Index getBitsForType(Type type) { - if (!type.isBasic()) { + if (!type.isConcrete()) { return -1; } - switch (type.getBasic()) { - case Type::i32: - return 32; - case Type::i64: - return 64; - default: - return -1; - } + return type.getByteSize() * 8; } // Useful information about locals diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index d3de5a54e93..fc32e288863 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -15142,8 +15142,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $gt_u-added-constant (param $x i32) - ;; A + 5 > 10 => A > 5, since this cannot overflow (A and the constants - ;; are small enough) + ;; x + C1 > C2 => x > (C2-C1), iff x+C1 and C2-C1 don't over/underflow (drop (i32.gt_u (i32.add @@ -15169,8 +15168,8 @@ (i32.const 5) ) ) - ;; If the first constant is larger, the result is trivial: this must be - ;; true. + ;; x + C1 > C2 => x + (C1-C2) > 0, iff x+C1 and C1-C2 don't over/underflow + ;; After doing that, further optimizations are possible here. (drop (i32.gt_u (i32.add From e97f00f82fafca2d5a10ba3f7ce87814d0c3789f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 8 Sep 2022 15:14:16 -0700 Subject: [PATCH 20/22] comment --- 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 a483fef6527..597742fff70 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3587,8 +3587,8 @@ struct OptimizeInstructions } // This is the second line above, we turn into x + (C1-C2) > 0. Other // optimizations can often kick in later. However, we must rule out the - // case where C2 is already 0 (to avoid continuing to think we are - // improving forever). + // case where C2 is already 0 (as then we would not actually change + // anything, and we could infinite loop). auto zero = Literal::makeZero(c2->type); if (c2->value != zero) { c1->value = c1->value.sub(c2->value); From 7584eb4c6e2dabde94428e0e88928e2f70a11244 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 8 Sep 2022 15:20:00 -0700 Subject: [PATCH 21/22] finish --- src/passes/OptimizeInstructions.cpp | 2 +- test/lit/passes/optimize-instructions.wast | 81 ++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 597742fff70..2c5f60c5bb9 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -52,7 +52,7 @@ namespace wasm { static Index getBitsForType(Type type) { - if (!type.isConcrete()) { + if (!type.isNumber()) { return -1; } return type.getByteSize() * 8; diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index fc32e288863..728b195de1a 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -15387,4 +15387,85 @@ ) ) ) + + ;; CHECK: (func $eq-added-constant (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (i32.shr_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 6) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $eq-added-constant (param $x i32) + ;; As above, but with eq rather than gt. We can optimize here. + (drop + (i32.eq + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 5) + ) + (i32.const 11) + ) + ) + ) + + ;; CHECK: (func $ne-added-constant (param $x i32) + ;; 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 6) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ne-added-constant (param $x i32) + ;; As above, but with ne rather than gt. We can optimize here. + (drop + (i32.ne + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 5) + ) + (i32.const 11) + ) + ) + ) + + ;; CHECK: (func $lt-added-constant (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.lt_u + ;; CHECK-NEXT: (i32.shr_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 6) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $lt-added-constant (param $x i32) + ;; As above, but with lt_s rather than gt_u. We can optimize here. + (drop + (i32.lt_s + (i32.add + (i32.shr_u + (local.get $x) + (i32.const 1) + ) + (i32.const 5) + ) + (i32.const 11) + ) + ) + ) ) From 73677437ad96031786c0b0bd051ea4c1e7fdc12e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 8 Sep 2022 15:22:51 -0700 Subject: [PATCH 22/22] note --- 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 2c5f60c5bb9..7f0b2857493 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -3569,7 +3569,7 @@ struct OptimizeInstructions } // x + C1 > C2 ==> x > (C2-C1) if no overflowing, C2 >= C1 // x + C1 > C2 ==> x + (C1-C2) > 0 if no overflowing, C2 < C1 - // And similarly for other relational operations. + // And similarly for other relational operations on integers. if (curr->isRelational()) { Binary* add; Const* c1;