From a48712bc5888ad42ebe94d562a51f2f15ad37900 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Fri, 5 Nov 2021 16:52:20 +0200 Subject: [PATCH 01/15] init --- src/passes/OptimizeInstructions.cpp | 19 ++++++ test/lit/passes/optimize-instructions.wast | 74 ++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index a52126c5a3e..cf901b1bd27 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -907,6 +907,25 @@ struct OptimizeInstructions } } + if (curr->op == ExtendUInt32 || curr->op == ExtendSInt32) { + if (auto* load = curr->value->dynCast()) { + if (!load->isAtomic) { + // i64.extend_i32_s(i32.load(_8|_16)(_u|_s)(x)) => + // i64.load(_8|_16|_32)(_u|_s)(x) + // + // i64.extend_i32_u(i32.load(_8|_16)(_u|_s)(x)) => + // i64.load(_8|_16|_32)(_u|_s)(x) + load->type = Type::i64; + if (load->bytes == 4) { + // Special case for i32.load. In this case signedness depends on + // extend operation. + load->signed_ = curr->op == ExtendSInt32; + } + return replaceCurrent(load); + } + } + } + if (Abstract::hasAnyReinterpret(curr->op)) { // i32.reinterpret_f32(f32.reinterpret_i32(x)) => x // i64.reinterpret_f64(f64.reinterpret_i64(x)) => x diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index 638811586a9..0d5a7314497 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -13404,4 +13404,78 @@ (drop (f32.reinterpret_i32 (i32.reinterpret_f32 (local.get $z)))) (drop (f64.reinterpret_i64 (i64.reinterpret_f64 (local.get $w)))) ) + + ;; u64(i32.load(_8|_16)(_u|_s)(x)) => i64.load(_8|_16|_32)(_u|_s)(x) + + ;; CHECK: (func $combine_load_and_extend_u (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load8_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load16_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load8_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load16_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load32_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $combine_load_and_extend_u (param $x i32) + (drop (i64.extend_i32_u (i32.load8_u (local.get $x)))) + (drop (i64.extend_i32_u (i32.load16_u (local.get $x)))) + (drop (i64.extend_i32_u (i32.load8_s (local.get $x)))) + (drop (i64.extend_i32_u (i32.load16_s (local.get $x)))) + (drop (i64.extend_i32_u (i32.load (local.get $x)))) + ) + + ;; i64(i32.load(_8|_16)(_u|_s)(x)) => i64.load(_8|_16|_32)(_u|_s)(x) + + ;; CHECK: (func $combine_load_and_extend_s (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load8_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load16_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load8_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load16_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.load32_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $combine_load_and_extend_s (param $x i32) + (drop (i64.extend_i32_s (i32.load8_u (local.get $x)))) + (drop (i64.extend_i32_s (i32.load16_u (local.get $x)))) + (drop (i64.extend_i32_s (i32.load8_s (local.get $x)))) + (drop (i64.extend_i32_s (i32.load16_s (local.get $x)))) + (drop (i64.extend_i32_s (i32.load (local.get $x)))) + ) ) From 7538df6f3ee6f58e9eb50ef5b50163a029d19c4d Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Fri, 5 Nov 2021 18:34:30 +0200 Subject: [PATCH 02/15] exclude two special cases --- src/passes/OptimizeInstructions.cpp | 6 +++++- test/lit/passes/optimize-instructions.wast | 21 +++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index cf901b1bd27..d01bf492e47 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -909,7 +909,11 @@ struct OptimizeInstructions if (curr->op == ExtendUInt32 || curr->op == ExtendSInt32) { if (auto* load = curr->value->dynCast()) { - if (!load->isAtomic) { + if (curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2) { + // Skip special this cases: + // i64.extend_i32_u(i32.load8_s(x)) + // i64.extend_i32_u(i32.load16_s(x)) + } else if (!load->isAtomic) { // i64.extend_i32_s(i32.load(_8|_16)(_u|_s)(x)) => // i64.load(_8|_16|_32)(_u|_s)(x) // diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index 0d5a7314497..26ef699d162 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -13406,6 +13406,9 @@ ) ;; u64(i32.load(_8|_16)(_u|_s)(x)) => i64.load(_8|_16|_32)(_u|_s)(x) + ;; except: + ;; i64.extend_i32_u(i32.load8_s(x)) and + ;; i64.extend_i32_u(i32.load16_s(x)) ;; CHECK: (func $combine_load_and_extend_u (param $x i32) ;; CHECK-NEXT: (drop @@ -13419,27 +13422,33 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.load8_s + ;; CHECK-NEXT: (i64.load32_u ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.load16_s - ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i64.extend_i32_u + ;; CHECK-NEXT: (i32.load8_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (i64.load32_u - ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i64.extend_i32_u + ;; CHECK-NEXT: (i32.load16_s + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $combine_load_and_extend_u (param $x i32) (drop (i64.extend_i32_u (i32.load8_u (local.get $x)))) (drop (i64.extend_i32_u (i32.load16_u (local.get $x)))) + (drop (i64.extend_i32_u (i32.load (local.get $x)))) + + ;; skips (drop (i64.extend_i32_u (i32.load8_s (local.get $x)))) (drop (i64.extend_i32_u (i32.load16_s (local.get $x)))) - (drop (i64.extend_i32_u (i32.load (local.get $x)))) ) ;; i64(i32.load(_8|_16)(_u|_s)(x)) => i64.load(_8|_16|_32)(_u|_s)(x) From 1b44333a242a439c3b6d88b54ca38646b9e85106 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Fri, 5 Nov 2021 18:36:34 +0200 Subject: [PATCH 03/15] refactor comments --- src/passes/OptimizeInstructions.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index d01bf492e47..ba1fec92490 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -909,16 +909,16 @@ struct OptimizeInstructions if (curr->op == ExtendUInt32 || curr->op == ExtendSInt32) { if (auto* load = curr->value->dynCast()) { + // i64.extend_i32_s(i32.load(_8|_16)(_u|_s)(x)) => + // i64.load(_8|_16|_32)(_u|_s)(x) + // + // i64.extend_i32_u(i32.load(_8|_16)(_u|_s)(x)) => + // i64.load(_8|_16|_32)(_u|_s)(x) if (curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2) { - // Skip special this cases: - // i64.extend_i32_u(i32.load8_s(x)) - // i64.extend_i32_u(i32.load16_s(x)) + // Skip special two special cases: + // i64.extend_i32_u(i32.load8_s(x)) and + // i64.extend_i32_u(i32.load16_s(x)) } else if (!load->isAtomic) { - // i64.extend_i32_s(i32.load(_8|_16)(_u|_s)(x)) => - // i64.load(_8|_16|_32)(_u|_s)(x) - // - // i64.extend_i32_u(i32.load(_8|_16)(_u|_s)(x)) => - // i64.load(_8|_16|_32)(_u|_s)(x) load->type = Type::i64; if (load->bytes == 4) { // Special case for i32.load. In this case signedness depends on From 3e848780c3d110527c26da1bb32cdf5e98ce64b0 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 10 Nov 2021 18:27:40 +0200 Subject: [PATCH 04/15] lift isAtomic check up --- src/passes/OptimizeInstructions.cpp | 34 +++++++++++++++-------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index d4ee17ad270..0b9628560e8 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -909,23 +909,25 @@ struct OptimizeInstructions if (curr->op == ExtendUInt32 || curr->op == ExtendSInt32) { if (auto* load = curr->value->dynCast()) { - // i64.extend_i32_s(i32.load(_8|_16)(_u|_s)(x)) => - // i64.load(_8|_16|_32)(_u|_s)(x) - // - // i64.extend_i32_u(i32.load(_8|_16)(_u|_s)(x)) => - // i64.load(_8|_16|_32)(_u|_s)(x) - if (curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2) { - // Skip special two special cases: - // i64.extend_i32_u(i32.load8_s(x)) and - // i64.extend_i32_u(i32.load16_s(x)) - } else if (!load->isAtomic) { - load->type = Type::i64; - if (load->bytes == 4) { - // Special case for i32.load. In this case signedness depends on - // extend operation. - load->signed_ = curr->op == ExtendSInt32; + if (!load->isAtomic) { + // i64.extend_i32_s(i32.load(_8|_16)(_u|_s)(x)) => + // i64.load(_8|_16|_32)(_u|_s)(x) + // + // i64.extend_i32_u(i32.load(_8|_16)(_u|_s)(x)) => + // i64.load(_8|_16|_32)(_u|_s)(x) + if (curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2) { + // Skip special two special cases: + // i64.extend_i32_u(i32.load8_s(x)) and + // i64.extend_i32_u(i32.load16_s(x)) + } else { + load->type = Type::i64; + if (load->bytes == 4) { + // Special case for i32.load. In this case signedness depends on + // extend operation. + load->signed_ = curr->op == ExtendSInt32; + } + return replaceCurrent(load); } - return replaceCurrent(load); } } } From 6ec5c89f263d7a7e4d490e0a2a3c6178b72ec51a Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 10 Nov 2021 18:29:12 +0200 Subject: [PATCH 05/15] simplify more --- src/passes/OptimizeInstructions.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 0b9628560e8..b73fc7f98f1 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -915,11 +915,11 @@ struct OptimizeInstructions // // i64.extend_i32_u(i32.load(_8|_16)(_u|_s)(x)) => // i64.load(_8|_16|_32)(_u|_s)(x) - if (curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2) { - // Skip special two special cases: - // i64.extend_i32_u(i32.load8_s(x)) and - // i64.extend_i32_u(i32.load16_s(x)) - } else { + // + // but skip special two special cases: + // i64.extend_i32_u(i32.load8_s(x)) and + // i64.extend_i32_u(i32.load16_s(x)) + if (!(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2)) { load->type = Type::i64; if (load->bytes == 4) { // Special case for i32.load. In this case signedness depends on From e807d3b8ac3748eea4fda7ef23a86de4e8cf1cf5 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 10 Nov 2021 18:30:38 +0200 Subject: [PATCH 06/15] more --- 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 b73fc7f98f1..75462a797f1 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -909,7 +909,6 @@ struct OptimizeInstructions if (curr->op == ExtendUInt32 || curr->op == ExtendSInt32) { if (auto* load = curr->value->dynCast()) { - if (!load->isAtomic) { // i64.extend_i32_s(i32.load(_8|_16)(_u|_s)(x)) => // i64.load(_8|_16|_32)(_u|_s)(x) // @@ -919,7 +918,8 @@ struct OptimizeInstructions // but skip special two special cases: // i64.extend_i32_u(i32.load8_s(x)) and // i64.extend_i32_u(i32.load16_s(x)) - if (!(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2)) { + if (!load->isAtomic && !(curr->op == ExtendUInt32 && load->signed_ && + load->bytes <= 2)) { load->type = Type::i64; if (load->bytes == 4) { // Special case for i32.load. In this case signedness depends on From 99a438572ca3c643337cf5f2854c3e7b005f0362 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 10 Nov 2021 20:49:43 +0200 Subject: [PATCH 07/15] fix --- src/passes/OptimizeInstructions.cpp | 35 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 75462a797f1..05d017ace4b 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -909,25 +909,24 @@ struct OptimizeInstructions if (curr->op == ExtendUInt32 || curr->op == ExtendSInt32) { if (auto* load = curr->value->dynCast()) { - // i64.extend_i32_s(i32.load(_8|_16)(_u|_s)(x)) => - // i64.load(_8|_16|_32)(_u|_s)(x) - // - // i64.extend_i32_u(i32.load(_8|_16)(_u|_s)(x)) => - // i64.load(_8|_16|_32)(_u|_s)(x) - // - // but skip special two special cases: - // i64.extend_i32_u(i32.load8_s(x)) and - // i64.extend_i32_u(i32.load16_s(x)) - if (!load->isAtomic && !(curr->op == ExtendUInt32 && load->signed_ && - load->bytes <= 2)) { - load->type = Type::i64; - if (load->bytes == 4) { - // Special case for i32.load. In this case signedness depends on - // extend operation. - load->signed_ = curr->op == ExtendSInt32; - } - return replaceCurrent(load); + // i64.extend_i32_s(i32.load(_8|_16)(_u|_s)(x)) => + // i64.load(_8|_16|_32)(_u|_s)(x) + // + // i64.extend_i32_u(i32.load(_8|_16)(_u|_s)(x)) => + // i64.load(_8|_16|_32)(_u|_s)(x) + // + // but skip special two special cases: + // i64.extend_i32_u(i32.load8_s(x)) and + // i64.extend_i32_u(i32.load16_s(x)) + if (!load->isAtomic && + !(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2)) { + load->type = Type::i64; + if (load->bytes == 4) { + // Special case for i32.load. In this case signedness depends on + // extend operation. + load->signed_ = curr->op == ExtendSInt32; } + return replaceCurrent(load); } } } From 6b81ffdf0b83b5c7c997f4c432474faf3946d9fe Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 10 Nov 2021 23:46:33 +0200 Subject: [PATCH 08/15] allow this for atomics as well --- 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 05d017ace4b..4d5cd042b08 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -918,8 +918,7 @@ struct OptimizeInstructions // but skip special two special cases: // i64.extend_i32_u(i32.load8_s(x)) and // i64.extend_i32_u(i32.load16_s(x)) - if (!load->isAtomic && - !(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2)) { + if (!(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2)) { load->type = Type::i64; if (load->bytes == 4) { // Special case for i32.load. In this case signedness depends on From 0892bb3006c00267421f47110707f1d0f81b9d91 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 11 Nov 2021 00:39:06 +0200 Subject: [PATCH 09/15] add comment about special cases --- src/passes/OptimizeInstructions.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 4d5cd042b08..1ec2ae91b38 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -915,9 +915,18 @@ struct OptimizeInstructions // i64.extend_i32_u(i32.load(_8|_16)(_u|_s)(x)) => // i64.load(_8|_16|_32)(_u|_s)(x) // - // but skip special two special cases: - // i64.extend_i32_u(i32.load8_s(x)) and - // i64.extend_i32_u(i32.load16_s(x)) + // but we can't do this in following cases: + // + // i64.extend_i32_u(i32.load8_s(x)) + // i64.extend_i32_u(i32.load16_s(x)) + // + // this mixed sign/zero extensions can't represent in single + // signed or unsigned load operation. For example if `load_s(x)` + // return i8(-1) (0xFF) than sign extended result will be + // i32(-1) (0xFFFFFFFF). + // But with zero extension to i64 we got 0x00000000FFFFFFFF. + // With `i64.load8_s` in this situation we got `i64(-1)` (all ones). + // With `i64.load8_u` in this will be `0x00000000000000FF`. if (!(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2)) { load->type = Type::i64; if (load->bytes == 4) { From 4efb6bcfae12c149ea12eb345692b98ef354bac4 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 11 Nov 2021 01:03:47 +0200 Subject: [PATCH 10/15] refactor comment --- src/passes/OptimizeInstructions.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 1ec2ae91b38..0d0aece9b98 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -923,10 +923,11 @@ struct OptimizeInstructions // this mixed sign/zero extensions can't represent in single // signed or unsigned load operation. For example if `load_s(x)` // return i8(-1) (0xFF) than sign extended result will be - // i32(-1) (0xFFFFFFFF). - // But with zero extension to i64 we got 0x00000000FFFFFFFF. - // With `i64.load8_s` in this situation we got `i64(-1)` (all ones). - // With `i64.load8_u` in this will be `0x00000000000000FF`. + // i32(-1) (0xFFFFFFFF) and with zero extension to i64 we got + // finally 0x00000000FFFFFFFF. However with `i64.load8_s` in this + // situation we got `i64(-1)` (all ones) and with `i64.load8_u` it + // will be 0x00000000000000FF. So no one simplify case can't satisfy + // 0x00000000FFFFFFFF. if (!(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2)) { load->type = Type::i64; if (load->bytes == 4) { From e702d9f31fbe44ba5124ffc49a81a168fa4c5385 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 11 Nov 2021 01:05:37 +0200 Subject: [PATCH 11/15] simplify --- src/passes/OptimizeInstructions.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 0d0aece9b98..6e68ad10bc2 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -921,13 +921,12 @@ struct OptimizeInstructions // i64.extend_i32_u(i32.load16_s(x)) // // this mixed sign/zero extensions can't represent in single - // signed or unsigned load operation. For example if `load_s(x)` + // signed or unsigned 64-bit load operation. For example if `load_s(x)` // return i8(-1) (0xFF) than sign extended result will be // i32(-1) (0xFFFFFFFF) and with zero extension to i64 we got // finally 0x00000000FFFFFFFF. However with `i64.load8_s` in this // situation we got `i64(-1)` (all ones) and with `i64.load8_u` it - // will be 0x00000000000000FF. So no one simplify case can't satisfy - // 0x00000000FFFFFFFF. + // will be 0x00000000000000FF. if (!(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2)) { load->type = Type::i64; if (load->bytes == 4) { From e9df929fb7304c72edee934cbecfd737d503e5d6 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 11 Nov 2021 01:06:08 +0200 Subject: [PATCH 12/15] fix --- 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 6e68ad10bc2..708e248f8a8 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -921,7 +921,7 @@ struct OptimizeInstructions // i64.extend_i32_u(i32.load16_s(x)) // // this mixed sign/zero extensions can't represent in single - // signed or unsigned 64-bit load operation. For example if `load_s(x)` + // signed or unsigned 64-bit load operation. For example if `load8_s(x)` // return i8(-1) (0xFF) than sign extended result will be // i32(-1) (0xFFFFFFFF) and with zero extension to i64 we got // finally 0x00000000FFFFFFFF. However with `i64.load8_s` in this From 62596581903eb522fdcd22c13f98337e8d3c95ac Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Fri, 12 Nov 2021 15:13:17 +0200 Subject: [PATCH 13/15] fix special case for sext(i64.atomic.load(x)). Add tests for atomics --- src/passes/OptimizeInstructions.cpp | 10 ++++- .../passes/optimize-instructions-atomics.wast | 44 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 708e248f8a8..50b7e60dce0 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -927,13 +927,19 @@ struct OptimizeInstructions // finally 0x00000000FFFFFFFF. However with `i64.load8_s` in this // situation we got `i64(-1)` (all ones) and with `i64.load8_u` it // will be 0x00000000000000FF. - if (!(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2)) { - load->type = Type::i64; + // + // Another limitation is atomics which only have unsigned loads. + // So we also avoid this only case: + // + // i64.extend_i32_s(i32.atomic.load(x)) + if (!(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2) && + !(load->isAtomic && load->bytes == 4 && curr->op == ExtendSInt32)) { if (load->bytes == 4) { // Special case for i32.load. In this case signedness depends on // extend operation. load->signed_ = curr->op == ExtendSInt32; } + load->type = Type::i64; return replaceCurrent(load); } } diff --git a/test/lit/passes/optimize-instructions-atomics.wast b/test/lit/passes/optimize-instructions-atomics.wast index 519c8399d48..979afde68a7 100644 --- a/test/lit/passes/optimize-instructions-atomics.wast +++ b/test/lit/passes/optimize-instructions-atomics.wast @@ -51,4 +51,48 @@ (drop (f64.reinterpret_i64 (i64.atomic.load (local.get $x)))) ;; skip (i32.atomic.store (i32.const 8) (i32.reinterpret_f32 (local.get $y))) ;; skip ) + + ;; CHECK: (func $combine_atomic_load_and_extends (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.atomic.load8_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.atomic.load16_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.atomic.load32_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.atomic.load8_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.atomic.load16_u + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i64.extend_i32_s + ;; CHECK-NEXT: (i32.atomic.load + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $combine_atomic_load_and_extends (param $x i32) + (drop (i64.extend_i32_u (i32.atomic.load8_u (local.get $x)))) + (drop (i64.extend_i32_u (i32.atomic.load16_u (local.get $x)))) + (drop (i64.extend_i32_u (i32.atomic.load (local.get $x)))) + (drop (i64.extend_i32_s (i32.atomic.load8_u (local.get $x)))) + (drop (i64.extend_i32_s (i32.atomic.load16_u (local.get $x)))) + ;; skips + (drop (i64.extend_i32_s (i32.atomic.load (local.get $x)))) + ) ) From 0245cc25c977af4831f5a6e23c77ac515a37955c Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Fri, 12 Nov 2021 20:01:06 +0200 Subject: [PATCH 14/15] refactor --- src/passes/OptimizeInstructions.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 50b7e60dce0..2efe910ed67 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -932,12 +932,13 @@ struct OptimizeInstructions // So we also avoid this only case: // // i64.extend_i32_s(i32.atomic.load(x)) - if (!(curr->op == ExtendUInt32 && load->signed_ && load->bytes <= 2) && - !(load->isAtomic && load->bytes == 4 && curr->op == ExtendSInt32)) { - if (load->bytes == 4) { + bool willBeSigned = curr->op == ExtendSInt32 && load->bytes == 4; + if (!(curr->op == ExtendUInt32 && load->bytes <= 2 && load->signed_) && + !(willBeSigned && load->isAtomic)) { + if (willBeSigned) { // Special case for i32.load. In this case signedness depends on // extend operation. - load->signed_ = curr->op == ExtendSInt32; + load->signed_ = true; } load->type = Type::i64; return replaceCurrent(load); From b07c84be4a3e4c6d3200ac0c5030ec9ab6344587 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Fri, 12 Nov 2021 22:57:07 +0200 Subject: [PATCH 15/15] move comment --- 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 2efe910ed67..7c450ac060a 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -932,12 +932,13 @@ struct OptimizeInstructions // So we also avoid this only case: // // i64.extend_i32_s(i32.atomic.load(x)) + + // Special case for i32.load. In this case signedness depends on + // extend operation. bool willBeSigned = curr->op == ExtendSInt32 && load->bytes == 4; if (!(curr->op == ExtendUInt32 && load->bytes <= 2 && load->signed_) && !(willBeSigned && load->isAtomic)) { if (willBeSigned) { - // Special case for i32.load. In this case signedness depends on - // extend operation. load->signed_ = true; } load->type = Type::i64;