From 2f94a006d6c203cdbf028b0d3355bc6ff5543b4e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 12:49:16 -0700 Subject: [PATCH 01/19] yolo --- src/passes/OptimizeInstructions.cpp | 104 ++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 7 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 047ed855134..7430f814899 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -853,15 +853,14 @@ struct OptimizeInstructions } } { - // i32.wrap_i64(i64.extend_i32_s(x)) => x + // i32.wrap_i64 can be removed, if the operations inside it do not + // actually require 64 bits, e.g.: + // // i32.wrap_i64(i64.extend_i32_u(x)) => x - Unary* inner; - Expression* x; if (matches(curr, - unary(WrapInt64, unary(&inner, ExtendSInt32, any(&x)))) || - matches(curr, - unary(WrapInt64, unary(&inner, ExtendUInt32, any(&x))))) { - return replaceCurrent(x); + unary(WrapInt64, any()))) { + if (auto* ret = optimizeWrappedResult(curr)) { + return replaceCurrent(ret); } } { @@ -2681,6 +2680,97 @@ struct OptimizeInstructions builder.makeConst(Literal::makeFromInt64(constant, walked->type))); } + // Given an i64.wrap operation, see if we can remove it. If all the things + // being operated on behave the same with or without wrapping, then we don't + // need to go to 64 bits at all, e.g.: + // + // int32_t(int64_t(x)) => x (extend and then wrap) + // int32_t(int64_t(x) + int64_t(10)) => x + int32_t(10) (also add + // + Expression* optimizeWrappedResult(Unary* wrap) { + assert(wrap->op == WrapInt64); + + // Core processing logic. This goes through the children, in one of two + // modes: + // * Scan: Find if there is anything we can't handle. Sets |canOptimize| + // with what it finds. + // * Optimize: Given we can handle everything, update things. This both + // updates the children of |wrap| and sets |replacement| which should + // replace |wrap|. + enum Mode { + Scan, + Optimize + }; + bool canOptimize = false + Expression* replacement = nullptr; + auto processChildren = [&](Mode mode) { + // Use a simple stack as we go through the children. We use ** as we need + // to replace children for some optimizations. + SmallVector stack; + stack.emplace_back(wrap); + + while (!stack.empty() && canOptimize) { + auto* currp = stack.pop_back(); + auto* curr = *currp; + if (curr->type == Type::unreachable) { + // Leave unreachability for other passes. + canOptimize = false; + } else if (auto* c = curr->dynCast()) { + // A i64 const can be handled by just turning it into an i32. + if (mode == Optimize) { + c->value = Literal(int32_t(c->value.getInteger())); + c->type = Type::i32; + } + } else if (auto* unary = curr->dynCast()) { + switch (unary->op) { + case ExtendSInt32: + case ExtendUInt32: { + // Note that there is nothing to push to the stack here: the child + // is 32-bit already, so we can stop looking. We just need to skip + // the extend operation. + if (mode == Optimize) { + *currp = unary->value; + } + break; + } + default: { + canOptimize = false; + } + } + } else if (auto* binary = curr->dynCast()) { + switch (binary->op) { + case AddInt64: + case SubInt64: + case MulInt64: { + if (mode == Optimize) { + // Turn the binary into a 32-bit one. + binary->op = Abstract::getBinary(c->type, Type::i32); + binary->type = Type::i32; + } + stack.push_back(binary->left); + stack.push_back(binary->right); + break; + } + default: { + canOptimize = false; + } + } + } else { + // Anything else makes us give up. + canOptimize = false; + } + } + }; + + processChildren(Scan); + if (!canOptimize) { + return nullptr; + } + + processChildren(Optimize); + return replacement; + } + // expensive1 | expensive2 can be turned into expensive1 ? 1 : expensive2, // and expensive | cheap can be turned into cheap ? 1 : expensive, // so that we can avoid one expensive computation, if it has no side effects. From 0222b334808eb17db46c8bdd3d68404b67d8b343 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 12:49:31 -0700 Subject: [PATCH 02/19] format --- src/passes/OptimizeInstructions.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 7430f814899..02b2700b0f8 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -857,12 +857,11 @@ struct OptimizeInstructions // actually require 64 bits, e.g.: // // i32.wrap_i64(i64.extend_i32_u(x)) => x - if (matches(curr, - unary(WrapInt64, any()))) { - if (auto* ret = optimizeWrappedResult(curr)) { - return replaceCurrent(ret); + if (matches(curr, unary(WrapInt64, any()))) { + if (auto* ret = optimizeWrappedResult(curr)) { + return replaceCurrent(ret); + } } - } { // i32.eqz(i32.wrap_i64(x)) => i64.eqz(x) // where maxBits(x) <= 32 @@ -2687,7 +2686,7 @@ struct OptimizeInstructions // int32_t(int64_t(x)) => x (extend and then wrap) // int32_t(int64_t(x) + int64_t(10)) => x + int32_t(10) (also add // - Expression* optimizeWrappedResult(Unary* wrap) { + Expression* optimizeWrappedResult(Unary * wrap) { assert(wrap->op == WrapInt64); // Core processing logic. This goes through the children, in one of two @@ -2697,12 +2696,8 @@ struct OptimizeInstructions // * Optimize: Given we can handle everything, update things. This both // updates the children of |wrap| and sets |replacement| which should // replace |wrap|. - enum Mode { - Scan, - Optimize - }; - bool canOptimize = false - Expression* replacement = nullptr; + enum Mode { Scan, Optimize }; + bool canOptimize = false Expression* replacement = nullptr; auto processChildren = [&](Mode mode) { // Use a simple stack as we go through the children. We use ** as we need // to replace children for some optimizations. From f0211d363c6fe60a8f366744808ec3c628a718da Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 12:50:45 -0700 Subject: [PATCH 03/19] fix --- src/passes/OptimizeInstructions.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 02b2700b0f8..c09e4fbb3a7 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2686,7 +2686,7 @@ struct OptimizeInstructions // int32_t(int64_t(x)) => x (extend and then wrap) // int32_t(int64_t(x) + int64_t(10)) => x + int32_t(10) (also add // - Expression* optimizeWrappedResult(Unary * wrap) { + Expression* optimizeWrappedResult(Unary* wrap) { assert(wrap->op == WrapInt64); // Core processing logic. This goes through the children, in one of two @@ -2697,7 +2697,8 @@ struct OptimizeInstructions // updates the children of |wrap| and sets |replacement| which should // replace |wrap|. enum Mode { Scan, Optimize }; - bool canOptimize = false Expression* replacement = nullptr; + bool canOptimize = false; + Expression* replacement = nullptr; auto processChildren = [&](Mode mode) { // Use a simple stack as we go through the children. We use ** as we need // to replace children for some optimizations. From c6fd2c04e4b1b2ed3db0b5a2db3e3279c7831e29 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 12:51:16 -0700 Subject: [PATCH 04/19] fix --- src/passes/OptimizeInstructions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index c09e4fbb3a7..c09a07fe098 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -862,6 +862,7 @@ struct OptimizeInstructions return replaceCurrent(ret); } } + } { // i32.eqz(i32.wrap_i64(x)) => i64.eqz(x) // where maxBits(x) <= 32 From fffb7e17a6d65da10abb3c2bc68e10ef3d6be427 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 12:52:10 -0700 Subject: [PATCH 05/19] fix --- 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 c09a07fe098..e2bd5f2d415 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2707,7 +2707,8 @@ struct OptimizeInstructions stack.emplace_back(wrap); while (!stack.empty() && canOptimize) { - auto* currp = stack.pop_back(); + auto* currp = stack.back(); + stack.pop_back(); auto* curr = *currp; if (curr->type == Type::unreachable) { // Leave unreachability for other passes. From e52ea2ce568f1626e61f3ab54d01c49494b04d7c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 12:57:35 -0700 Subject: [PATCH 06/19] fix --- src/passes/OptimizeInstructions.cpp | 36 +++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index e2bd5f2d415..e52002e74f9 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2713,6 +2713,7 @@ struct OptimizeInstructions if (curr->type == Type::unreachable) { // Leave unreachability for other passes. canOptimize = false; + return; } else if (auto* c = curr->dynCast()) { // A i64 const can be handled by just turning it into an i32. if (mode == Optimize) { @@ -2733,25 +2734,46 @@ struct OptimizeInstructions } default: { canOptimize = false; + return; } } } else if (auto* binary = curr->dynCast()) { + // Turn the binary into a 32-bit one, if we can. switch (binary->op) { case AddInt64: case SubInt64: case MulInt64: { - if (mode == Optimize) { - // Turn the binary into a 32-bit one. - binary->op = Abstract::getBinary(c->type, Type::i32); - binary->type = Type::i32; - } - stack.push_back(binary->left); - stack.push_back(binary->right); + // We can optimize these. break; } default: { canOptimize = false; + return; + } + } + if (mode == Optimize) { + switch (binary->op) { + case AddInt64: { + binary->op = AddInt32; + break; + } + case SubInt64: { + binary->op = SubInt32; + break; + } + case MulInt64: { + binary->op = MulInt32; + break; + } + default: { + WASM_UNREACHABLE("bad op"); + } } + // All things we can optimize do the following: change the type to + // i32, and prepare to scan the children. + binary->type = Type::i32; + stack.push_back(&binary->left); + stack.push_back(&binary->right); } } else { // Anything else makes us give up. From 212735df1957c051eaedce23506df41bf39afe73 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 12:58:30 -0700 Subject: [PATCH 07/19] fix --- 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 e52002e74f9..5fea4e89b17 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2704,7 +2704,8 @@ struct OptimizeInstructions // Use a simple stack as we go through the children. We use ** as we need // to replace children for some optimizations. SmallVector stack; - stack.emplace_back(wrap); + Expression* top = wrap; + stack.emplace_back(&top); while (!stack.empty() && canOptimize) { auto* currp = stack.back(); From a1df6b093399c2b78cf6133dfc00050889171921 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 13:09:25 -0700 Subject: [PATCH 08/19] fix --- src/passes/OptimizeInstructions.cpp | 14 ++++++------- test/lit/passes/optimize-instructions.wast | 24 ++++++++++++++++++---- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 5fea4e89b17..d35911c93e0 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2694,18 +2694,14 @@ struct OptimizeInstructions // modes: // * Scan: Find if there is anything we can't handle. Sets |canOptimize| // with what it finds. - // * Optimize: Given we can handle everything, update things. This both - // updates the children of |wrap| and sets |replacement| which should - // replace |wrap|. + // * Optimize: Given we can handle everything, update things. enum Mode { Scan, Optimize }; - bool canOptimize = false; - Expression* replacement = nullptr; + bool canOptimize = true; auto processChildren = [&](Mode mode) { // Use a simple stack as we go through the children. We use ** as we need // to replace children for some optimizations. SmallVector stack; - Expression* top = wrap; - stack.emplace_back(&top); + stack.emplace_back(&wrap->value); while (!stack.empty() && canOptimize) { auto* currp = stack.back(); @@ -2788,8 +2784,10 @@ struct OptimizeInstructions return nullptr; } + // Optimize, and return the optimized results (in which we no longer need + // the wrap operation itself). processChildren(Optimize); - return replacement; + return wrap->value; } // expensive1 | expensive2 can be turned into expensive1 ? 1 : expensive2, diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index 71d4e7fe3e3..a000d96b994 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -3306,7 +3306,11 @@ ;; CHECK-NEXT: (i32.shr_s ;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (i32.shr_u - ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.extend_i32_s + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const 24) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const 24) @@ -3332,7 +3336,11 @@ ) ;; CHECK: (func $sext-24-shr_u-wrap-extend (result i32) ;; CHECK-NEXT: (i32.shr_u - ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.extend_i32_s + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const 25) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -13051,10 +13059,18 @@ ;; CHECK: (func $sign-and-zero-extention-elimination-1 (param $x i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.extend_i32_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.extend_i32_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $sign-and-zero-extention-elimination-1 (param $x i32) From 600cd1223c84a7237ac6bc24919a2475504e37c3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 13:10:38 -0700 Subject: [PATCH 09/19] update --- test/lit/passes/optimize-instructions.wast | 81 +++++----------------- 1 file changed, 19 insertions(+), 62 deletions(-) diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index a000d96b994..b3091607d53 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -1200,27 +1200,27 @@ (i32.store16 (i32.const 11) (i32.and (i32.const -4) (i32.const 65534))) ) ;; CHECK: (func $store8-wrap - ;; CHECK-NEXT: (i64.store8 + ;; CHECK-NEXT: (i32.store8 ;; CHECK-NEXT: (i32.const 11) - ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $store8-wrap (i32.store8 (i32.const 11) (i32.wrap_i64 (i64.const 1))) ) ;; CHECK: (func $store16-wrap - ;; CHECK-NEXT: (i64.store16 + ;; CHECK-NEXT: (i32.store16 ;; CHECK-NEXT: (i32.const 11) - ;; CHECK-NEXT: (i64.const 2) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $store16-wrap (i32.store16 (i32.const 11) (i32.wrap_i64 (i64.const 2))) ) ;; CHECK: (func $store-wrap - ;; CHECK-NEXT: (i64.store32 + ;; CHECK-NEXT: (i32.store ;; CHECK-NEXT: (i32.const 11) - ;; CHECK-NEXT: (i64.const 3) + ;; CHECK-NEXT: (i32.const 3) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $store-wrap @@ -3256,9 +3256,7 @@ ;; CHECK-NEXT: (i32.shr_s ;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (i32.shr_u - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.const -1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -1) ;; CHECK-NEXT: (i32.const 24) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const 24) @@ -3282,9 +3280,7 @@ ) ;; CHECK: (func $sext-24-shr_u-wrap (result i32) ;; CHECK-NEXT: (i32.shr_u - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.const -1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -1) ;; CHECK-NEXT: (i32.const 25) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -3306,11 +3302,7 @@ ;; CHECK-NEXT: (i32.shr_s ;; CHECK-NEXT: (i32.shl ;; CHECK-NEXT: (i32.shr_u - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.extend_i32_s - ;; CHECK-NEXT: (i32.const -1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -1) ;; CHECK-NEXT: (i32.const 24) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const 24) @@ -3336,11 +3328,7 @@ ) ;; CHECK: (func $sext-24-shr_u-wrap-extend (result i32) ;; CHECK-NEXT: (i32.shr_u - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.extend_i32_s - ;; CHECK-NEXT: (i32.const -1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -1) ;; CHECK-NEXT: (i32.const 25) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -6415,15 +6403,7 @@ ) ) ;; CHECK: (func $optimizeAddedConstants-filters-through-nonzero (result i32) - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (i32.shl - ;; CHECK-NEXT: (i32.const -536870912) - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 31744) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -536902656) ;; CHECK-NEXT: ) (func $optimizeAddedConstants-filters-through-nonzero (result i32) (i32.sub @@ -6440,15 +6420,7 @@ ) ) ;; CHECK: (func $optimizeAddedConstants-filters-through-nonzero-b (result i32) - ;; CHECK-NEXT: (i32.sub - ;; CHECK-NEXT: (i32.shl - ;; CHECK-NEXT: (i32.const -536870912) - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.const -1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.const 31744) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -31744) ;; CHECK-NEXT: ) (func $optimizeAddedConstants-filters-through-nonzero-b (result i32) (i32.sub @@ -6618,19 +6590,12 @@ ;; CHECK-NEXT: (i32.eqz ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.const 2) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.eqz - ;; CHECK-NEXT: (local.get $y) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.const 1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -12551,8 +12516,8 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.eqz - ;; CHECK-NEXT: (i64.const 1) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop @@ -13059,18 +13024,10 @@ ;; CHECK: (func $sign-and-zero-extention-elimination-1 (param $x i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.extend_i32_s - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.extend_i32_u - ;; CHECK-NEXT: (local.get $x) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $sign-and-zero-extention-elimination-1 (param $x i32) From 71555475bad0deafddcb2139a738375355cbbbec Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 13:12:38 -0700 Subject: [PATCH 10/19] work --- src/passes/OptimizeInstructions.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index d35911c93e0..58a4a1257ec 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1169,11 +1169,7 @@ struct OptimizeInstructions optimizeMemoryAccess(curr->ptr, curr->offset, curr->memory); optimizeStoredValue(curr->value, curr->bytes); if (auto* unary = curr->value->dynCast()) { - if (unary->op == WrapInt64) { - // instead of wrapping to 32, just store some of the bits in the i64 - curr->valueType = Type::i64; - curr->value = unary->value; - } else if (!curr->isAtomic && Abstract::hasAnyReinterpret(unary->op) && + if (!curr->isAtomic && Abstract::hasAnyReinterpret(unary->op) && curr->bytes == curr->valueType.getByteSize()) { // f32.store(y, f32.reinterpret_i32(x)) => i32.store(y, x) // f64.store(y, f64.reinterpret_i64(x)) => i64.store(y, x) From 9590a0d4968e24ccb695373b0080fc5ade2b3ac0 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 13:15:32 -0700 Subject: [PATCH 11/19] format --- 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 58a4a1257ec..e959bdca9dc 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1170,7 +1170,7 @@ struct OptimizeInstructions optimizeStoredValue(curr->value, curr->bytes); if (auto* unary = curr->value->dynCast()) { if (!curr->isAtomic && Abstract::hasAnyReinterpret(unary->op) && - curr->bytes == curr->valueType.getByteSize()) { + curr->bytes == curr->valueType.getByteSize()) { // f32.store(y, f32.reinterpret_i32(x)) => i32.store(y, x) // f64.store(y, f64.reinterpret_i64(x)) => i64.store(y, x) // i32.store(y, i32.reinterpret_f32(x)) => f32.store(y, x) From 59d60eacaf58aecbe8a783961dd92fdf1245c85f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 13:24:48 -0700 Subject: [PATCH 12/19] test --- test/lit/passes/optimize-instructions.wast | 124 +++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index b3091607d53..c400d7dbc83 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -14719,4 +14719,128 @@ (drop (i64.extend_i32_s (i32.load16_s (local.get $x)))) (drop (i64.extend_i32_s (i32.load (local.get $x)))) ) + + ;; CHECK: (func $wrap-i64-to-i32-add (param $x i32) (result i32) + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 8) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $wrap-i64-to-i32-add (param $x i32) (result i32) + ;; Rather than extend to 64 and add there, we can do all of this in i32. + (i32.wrap_i64 + (i64.add + (i64.extend_i32_u + (local.get $x) + ) + (i64.const 8) + ) + ) + ) + + ;; CHECK: (func $wrap-i64-to-i32-sub (param $x i32) (result i32) + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (i32.const 8) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $wrap-i64-to-i32-sub (param $x i32) (result i32) + (i32.wrap_i64 + (i64.sub + (i64.const 8) + (i64.extend_i32_u + (local.get $x) + ) + ) + ) + ) + + ;; CHECK: (func $wrap-i64-to-i32-mul (param $x i32) (result i32) + ;; CHECK-NEXT: (i32.mul + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $wrap-i64-to-i32-mul (param $x i32) (result i32) + (i32.wrap_i64 + (i64.mul + (i64.extend_i32_u + (local.get $x) + ) + (i64.const 42) + ) + ) + ) + + ;; CHECK: (func $wrap-i64-to-i32-many (param $x i32) (param $y i32) (result i32) + ;; CHECK-NEXT: (i32.mul + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.sub + ;; CHECK-NEXT: (i32.const -1) + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $wrap-i64-to-i32-many (param $x i32) (param $y i32) (result i32) + ;; Multiple operations that all together can be turned into i32. + (i32.wrap_i64 + (i64.mul + (i64.add + (i64.extend_i32_u + (local.get $x) + ) + (i64.sub + (i64.const -1) + (i64.extend_i32_u + (local.get $y) + ) + ) + ) + (i64.const 42) + ) + ) + ) + + ;; CHECK: (func $wrap-i64-to-i32-div-no (param $x i32) (result i32) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.div_u + ;; CHECK-NEXT: (i64.extend_i32_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i64.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $wrap-i64-to-i32-div-no (param $x i32) (result i32) + ;; We *cannot* optimize here, as division cares about i32/i64 differences. + (i32.wrap_i64 + (i64.div_s + (i64.extend_i32_u + (local.get $x) + ) + (i64.const 42) + ) + ) + ) + + ;; CHECK: (func $wrap-i64-to-i32-local-no (param $x i64) (result i32) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.div_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i64.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $wrap-i64-to-i32-local-no (param $x i64) (result i32) + ;; We do not optimize here for now as an input ($x) is an i64. TODO + (i32.wrap_i64 + (i64.div_s + (local.get $x) + (i64.const 42) + ) + ) + ) ) From 81f899b3039f97ecbfb7076202c94192b0d86d4e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 14:06:54 -0700 Subject: [PATCH 13/19] fix --- src/passes/OptimizeInstructions.cpp | 5 ++-- test/lit/passes/optimize-instructions.wast | 28 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index e959bdca9dc..a534aa2337b 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2765,12 +2765,13 @@ struct OptimizeInstructions // All things we can optimize do the following: change the type to // i32, and prepare to scan the children. binary->type = Type::i32; - stack.push_back(&binary->left); - stack.push_back(&binary->right); } + stack.push_back(&binary->left); + stack.push_back(&binary->right); } else { // Anything else makes us give up. canOptimize = false; + return; } } }; diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index c400d7dbc83..1a6c292db9b 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -14826,6 +14826,34 @@ ) ) + ;; CHECK: (func $wrap-i64-to-i32-tee-no (param $x i32) (result i32) + ;; CHECK-NEXT: (local $y i64) + ;; CHECK-NEXT: (i32.wrap_i64 + ;; CHECK-NEXT: (i64.add + ;; CHECK-NEXT: (local.tee $y + ;; CHECK-NEXT: (i64.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i64.extend_i32_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $wrap-i64-to-i32-tee-no (param $x i32) (result i32) + ;; The local.tee stops us from optimizing atm. TODO + (local $y i64) + (i32.wrap_i64 + (i64.add + (i64.extend_i32_u + (local.get $x) + ) + (local.tee $y + (i64.const 42) + ) + ) + ) + ) + ;; CHECK: (func $wrap-i64-to-i32-local-no (param $x i64) (result i32) ;; CHECK-NEXT: (i32.wrap_i64 ;; CHECK-NEXT: (i64.div_s From 4c973eaa6e407a25d0afe7b4726c350c164baafa Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 14:15:31 -0700 Subject: [PATCH 14/19] fix --- test/lit/passes/optimize-instructions.wast | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index 8da60de6a93..171972fea1f 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -3252,9 +3252,7 @@ ;; CHECK: (func $sext-24-shr_u-wrap-too-big (result i32) ;; CHECK-NEXT: (i32.shr_s ;; CHECK-NEXT: (i32.and - ;; CHECK-NEXT: (i32.wrap_i64 - ;; CHECK-NEXT: (i64.const -1) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.const -1) ;; CHECK-NEXT: (i32.const -16777216) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (i32.const 24) From 7386378dfdee84a9e52d584e9b6c3416b578f897 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 14:23:28 -0700 Subject: [PATCH 15/19] comments --- 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 d159c7455dd..3318801c25b 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2680,8 +2680,8 @@ struct OptimizeInstructions // being operated on behave the same with or without wrapping, then we don't // need to go to 64 bits at all, e.g.: // - // int32_t(int64_t(x)) => x (extend and then wrap) - // int32_t(int64_t(x) + int64_t(10)) => x + int32_t(10) (also add + // int32_t(int64_t(x)) => x (extend, then wrap) + // int32_t(int64_t(x) + int64_t(10)) => x + int32_t(10) (also add) // Expression* optimizeWrappedResult(Unary* wrap) { assert(wrap->op == WrapInt64); From 90ff00cef452d53d0ad527dcf7ddd1956233935d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 14:24:45 -0700 Subject: [PATCH 16/19] comments --- 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 3318801c25b..49c13d447a8 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2762,8 +2762,7 @@ struct OptimizeInstructions WASM_UNREACHABLE("bad op"); } } - // All things we can optimize do the following: change the type to - // i32, and prepare to scan the children. + // All things we can optimize change the type to i32. binary->type = Type::i32; } stack.push_back(&binary->left); From 13180f92d5173c41bf4a8f7bd90be05e3c26c2f8 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 15:37:57 -0700 Subject: [PATCH 17/19] Update src/passes/OptimizeInstructions.cpp Co-authored-by: Thomas Lively <7121787+tlively@users.noreply.github.com> --- 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 49c13d447a8..123884988ca 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -853,7 +853,7 @@ struct OptimizeInstructions } } { - // i32.wrap_i64 can be removed, if the operations inside it do not + // i32.wrap_i64 can be removed if the operations inside it do not // actually require 64 bits, e.g.: // // i32.wrap_i64(i64.extend_i32_u(x)) => x From c7db4d3457e6c3847c85767e3497d9ff2d8534b6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 15:41:08 -0700 Subject: [PATCH 18/19] fix --- src/passes/OptimizeInstructions.cpp | 8 ++++-- test/lit/passes/optimize-instructions.wast | 30 +++++++++++----------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 49c13d447a8..0196ad0a0c6 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1169,8 +1169,12 @@ struct OptimizeInstructions optimizeMemoryAccess(curr->ptr, curr->offset, curr->memory); optimizeStoredValue(curr->value, curr->bytes); if (auto* unary = curr->value->dynCast()) { - if (!curr->isAtomic && Abstract::hasAnyReinterpret(unary->op) && - curr->bytes == curr->valueType.getByteSize()) { + if (unary->op == WrapInt64) { + // instead of wrapping to 32, just store some of the bits in the i64 + curr->valueType = Type::i64; + curr->value = unary->value; + } else if (!curr->isAtomic && Abstract::hasAnyReinterpret(unary->op) && + curr->bytes == curr->valueType.getByteSize()) { // f32.store(y, f32.reinterpret_i32(x)) => i32.store(y, x) // f64.store(y, f64.reinterpret_i64(x)) => i64.store(y, x) // i32.store(y, i32.reinterpret_f32(x)) => f32.store(y, x) diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index 171972fea1f..d8c72bb65ef 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -1199,32 +1199,32 @@ (func $store16-and-65534 (i32.store16 (i32.const 11) (i32.and (i32.const -4) (i32.const 65534))) ) - ;; CHECK: (func $store8-wrap - ;; CHECK-NEXT: (i32.store8 + ;; CHECK: (func $store8-wrap (param $x i64) + ;; CHECK-NEXT: (i64.store8 ;; CHECK-NEXT: (i32.const 11) - ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $store8-wrap - (i32.store8 (i32.const 11) (i32.wrap_i64 (i64.const 1))) + (func $store8-wrap (param $x i64) + (i32.store8 (i32.const 11) (i32.wrap_i64 (local.get $x))) ) - ;; CHECK: (func $store16-wrap - ;; CHECK-NEXT: (i32.store16 + ;; CHECK: (func $store16-wrap (param $x i64) + ;; CHECK-NEXT: (i64.store16 ;; CHECK-NEXT: (i32.const 11) - ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $store16-wrap - (i32.store16 (i32.const 11) (i32.wrap_i64 (i64.const 2))) + (func $store16-wrap (param $x i64) + (i32.store16 (i32.const 11) (i32.wrap_i64 (local.get $x))) ) - ;; CHECK: (func $store-wrap - ;; CHECK-NEXT: (i32.store + ;; CHECK: (func $store-wrap (param $x i64) + ;; CHECK-NEXT: (i64.store32 ;; CHECK-NEXT: (i32.const 11) - ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $store-wrap - (i32.store (i32.const 11) (i32.wrap_i64 (i64.const 3))) + (func $store-wrap (param $x i64) + (i32.store (i32.const 11) (i32.wrap_i64 (local.get $x))) ) ;; CHECK: (func $store8-neg1 ;; CHECK-NEXT: (i32.store8 From 744df5686bd40c9e66179f034c1c0ef4616d6e22 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 6 Sep 2022 15:44:47 -0700 Subject: [PATCH 19/19] TODO --- src/passes/OptimizeInstructions.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 0196ad0a0c6..27f2afa9d2c 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2730,6 +2730,8 @@ struct OptimizeInstructions break; } default: { + // TODO: handle more cases here and below, + // https://github.com/WebAssembly/binaryen/issues/5004 canOptimize = false; return; }