From 3459b7ee77a32a6e837f766af16fbf195d7677a2 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 23 May 2020 13:06:25 +0300 Subject: [PATCH 01/25] more optimizations for pow of two consts --- src/passes/OptimizeInstructions.cpp | 60 +++++++++++++++++-- .../optimize-instructions_all-features.txt | 58 ++++++++++++++++++ .../optimize-instructions_all-features.wast | 59 ++++++++++++++++++ test/unit.fromasm.imprecise | 5 +- 4 files changed, 174 insertions(+), 8 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 3e3863e954b..4987b0acc0d 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -578,10 +578,28 @@ struct OptimizeInstructions if (right->type == Type::i32) { uint32_t c = right->value.geti32(); if (IsPowerOf2(c)) { - if (binary->op == MulInt32) { - return optimizePowerOf2Mul(binary, c); - } else if (binary->op == RemUInt32) { - return optimizePowerOf2URem(binary, c); + switch (binary->op) { + case MulInt32: + return optimizePowerOf2Mul(binary, c); + case RemUInt32: + return optimizePowerOf2URem(binary, c); + case DivUInt32: + return optimizePowerOf2UDiv(binary, c); + default: + break; + } + } + } + if (right->type == Type::i64) { + uint64_t c = right->value.geti64(); + if (IsPowerOf2(c)) { + switch (binary->op) { + // TODO: + case MulInt64: + case RemUInt64: + case DivUInt64: + default: + break; } } } @@ -1272,6 +1290,18 @@ struct OptimizeInstructions return binary; } + // Optimize an unsigned divide by a power of two on the right + // This doesn't shrink code size, and VMs likely optimize it anyhow, + // but it's still worth doing since + // * Usually ands are more common than urems. + // * The constant is slightly smaller. + Expression* optimizePowerOf2UDiv(Binary* binary, uint32_t c) { + uint32_t shifts = CountTrailingZeroes(c); + binary->op = ShrUInt32; + binary->right->cast()->value = Literal(int32_t(shifts)); + return binary; + } + // Optimize an unsigned divide by a power of two on the right, // which can be an AND mask // This doesn't shrink code size, and VMs likely optimize it anyhow, @@ -1354,6 +1384,28 @@ struct OptimizeInstructions !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { return binary->right; + } else if (binary->op == Abstract::getBinary(type, Abstract::RemS) && + !EffectAnalyzer(getPassOptions(), features, binary->left) + .hasSideEffects()) { + // (signed)x % -1 ==> 0 + return LiteralUtils::makeZero(type, *getModule()); + } else if (binary->op == Abstract::getBinary(type, Abstract::DivU) && + !EffectAnalyzer(getPassOptions(), features, binary->left) + .hasSideEffects()) { + // (unsigned)x / -1 ==> x != -1 + return Builder(*getModule()) + .makeBinary(Abstract::getBinary(type, Abstract::Eq), + binary->right, + binary->left); + } else if (binary->op == Abstract::getBinary(type, Abstract::DivS) && + !EffectAnalyzer(getPassOptions(), features, binary->left) + .hasSideEffects()) { + // (signed)x / -1 ==> 0 - x + Builder builder(*getModule()); + return builder.makeBinary( + Abstract::getBinary(type, Abstract::Sub), + builder.makeConst(Literal::makeFromInt32(0, type)), + binary->left); } } // wasm binary encoding uses signed LEBs, which slightly favor negative diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index d84095b89b7..6af140731e1 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -2576,6 +2576,64 @@ ) (unreachable) ) + (func $div-32-power-2 (param $x i32) (result i32) + (drop + (call $div-32-power-2 + (i32.shr_u + (local.get $x) + (i32.const 2) + ) + ) + ) + (drop + (call $div-32-power-2 + (i32.div_u + (local.get $x) + (i32.const 5) + ) + ) + ) + (drop + (call $div-32-power-2 + (local.get $x) + ) + ) + (drop + (call $div-32-power-2 + (i32.div_u + (local.get $x) + (i32.const 0) + ) + ) + ) + (drop + (call $div-32-power-2 + (i32.div_u + (call $div-32-power-2 + (i32.const 123) + ) + (i32.const 0) + ) + ) + ) + (drop + (call $div-32-power-2 + (i32.eq + (local.get $x) + (i32.const -1) + ) + ) + ) + (drop + (call $div-32-power-2 + (i32.shr_u + (local.get $x) + (i32.const 31) + ) + ) + ) + (unreachable) + ) (func $urem-power-2 (param $x i32) (result i32) (drop (call $urem-power-2 diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index 9694d9b2871..6921bdaa53c 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -2915,6 +2915,65 @@ ) (unreachable) ) + (func $div-32-power-2 (param $x i32) (result i32) + (drop + (call $div-32-power-2 + (i32.div_u + (local.get $x) + (i32.const 4) + ) + ) + ) + (drop + (call $div-32-power-2 + (i32.div_u + (local.get $x) + (i32.const 5) + ) + ) + ) + (drop + (call $div-32-power-2 + (i32.div_u + (local.get $x) + (i32.const 1) + ) + ) + ) + (drop + (call $div-32-power-2 + (i32.div_u + (local.get $x) + (i32.const 0) + ) + ) + ) + (drop + (call $div-32-power-2 + (i32.div_u + (call $div-32-power-2 (i32.const 123)) ;; side effects + (i32.const 0) + ) + ) + ) + (drop + (call $div-32-power-2 + (i32.div_u + (local.get $x) + (i32.const 0xffffffff) + ) + ) + ) + (drop + (call $div-32-power-2 + (i32.div_u + (local.get $x) + (i32.const 0x80000000) + ) + ) + ) + (unreachable) + ) (func $urem-power-2 (param $x i32) (result i32) (drop (call $urem-power-2 diff --git a/test/unit.fromasm.imprecise b/test/unit.fromasm.imprecise index afbaea82afc..48cef072918 100644 --- a/test/unit.fromasm.imprecise +++ b/test/unit.fromasm.imprecise @@ -220,10 +220,7 @@ (i32.const 2147483647) ) (func $trapping_sint_div_s (; has Stack IR ;) (result i32) - (i32.div_s - (i32.const -2147483648) - (i32.const -1) - ) + (i32.const -2147483648) ) (func $fr (; has Stack IR ;) (param $0 f32) (nop) From 5d4d963188c10ffd2ee04dccd26af58b79422387 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 23 May 2020 14:35:28 +0300 Subject: [PATCH 02/25] add case for x * -1 --- src/passes/OptimizeInstructions.cpp | 4 +++- test/passes/optimize-instructions_all-features.txt | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 4987b0acc0d..be169c593d8 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1397,10 +1397,12 @@ struct OptimizeInstructions .makeBinary(Abstract::getBinary(type, Abstract::Eq), binary->right, binary->left); - } else if (binary->op == Abstract::getBinary(type, Abstract::DivS) && + } else if ((binary->op == Abstract::getBinary(type, Abstract::DivS) || + binary->op == Abstract::getBinary(type, Abstract::Mul)) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { // (signed)x / -1 ==> 0 - x + // (signed)x * -1 ==> 0 - x Builder builder(*getModule()); return builder.makeBinary( Abstract::getBinary(type, Abstract::Sub), diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index 6af140731e1..1481469e4db 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -2560,9 +2560,9 @@ ) (drop (call $mul-power-2 - (i32.mul + (i32.sub + (i32.const 0) (local.get $x) - (i32.const -1) ) ) ) @@ -2797,15 +2797,15 @@ ) ) (drop - (i32.mul + (i32.sub + (i32.const 0) (local.get $x32) - (i32.const -1) ) ) (drop - (i64.mul + (i64.sub + (i64.const 0) (local.get $x64) - (i64.const -1) ) ) (drop From d0d7813238569acd969832f0c8a3c79e6f54db59 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 23 May 2020 14:45:51 +0300 Subject: [PATCH 03/25] don't check sideeffects for cases which save left expr --- src/passes/OptimizeInstructions.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index be169c593d8..aa814b1579a 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1379,30 +1379,28 @@ struct OptimizeInstructions if (right->value == Literal(int32_t(-1)) || right->value == Literal(int64_t(-1))) { if (binary->op == Abstract::getBinary(type, Abstract::And)) { + // x & -1 ==> x return binary->left; } else if (binary->op == Abstract::getBinary(type, Abstract::Or) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { + // x & -1 ==> -1 return binary->right; } else if (binary->op == Abstract::getBinary(type, Abstract::RemS) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { - // (signed)x % -1 ==> 0 + // (signed)x % -1 ==> 0 return LiteralUtils::makeZero(type, *getModule()); - } else if (binary->op == Abstract::getBinary(type, Abstract::DivU) && - !EffectAnalyzer(getPassOptions(), features, binary->left) - .hasSideEffects()) { - // (unsigned)x / -1 ==> x != -1 + } else if (binary->op == Abstract::getBinary(type, Abstract::DivU)) { + // (unsigned)x / -1 ==> x != -1 return Builder(*getModule()) .makeBinary(Abstract::getBinary(type, Abstract::Eq), binary->right, binary->left); } else if ((binary->op == Abstract::getBinary(type, Abstract::DivS) || - binary->op == Abstract::getBinary(type, Abstract::Mul)) && - !EffectAnalyzer(getPassOptions(), features, binary->left) - .hasSideEffects()) { - // (signed)x / -1 ==> 0 - x - // (signed)x * -1 ==> 0 - x + binary->op == Abstract::getBinary(type, Abstract::Mul))) { + // (signed)x / -1 ==> 0 - x + // (signed)x * -1 ==> 0 - x Builder builder(*getModule()); return builder.makeBinary( Abstract::getBinary(type, Abstract::Sub), From 99936bd5255c84d5cff8a874e771f3c50e583501 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 23 May 2020 15:17:24 +0300 Subject: [PATCH 04/25] add case for x - (-1) => x + 1 --- src/passes/OptimizeInstructions.cpp | 7 +++++++ .../optimize-instructions_all-features.txt | 16 +++++++++++++++- .../optimize-instructions_all-features.wast | 10 ++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index aa814b1579a..ee61450458e 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1386,6 +1386,13 @@ struct OptimizeInstructions .hasSideEffects()) { // x & -1 ==> -1 return binary->right; + } else if (binary->op == Abstract::getBinary(type, Abstract::Sub)) { + // x - (-1) ==> x + 1 + Builder builder(*getModule()); + return builder + .makeBinary(Abstract::getBinary(type, Abstract::Add), + binary->left, + builder.makeConst(Literal::makeFromInt32(1, type))); } else if (binary->op == Abstract::getBinary(type, Abstract::RemS) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index 1481469e4db..6a0760e7c76 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -3,9 +3,9 @@ (type $i32_=>_i32 (func (param i32) (result i32))) (type $none_=>_i32 (func (result i32))) (type $none_=>_none (func)) + (type $i32_i64_=>_none (func (param i32 i64))) (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) (type $i32_=>_none (func (param i32))) - (type $i32_i64_=>_none (func (param i32 i64))) (type $none_=>_i64 (func (result i64))) (type $i32_i64_f32_=>_none (func (param i32 i64 f32))) (type $i64_=>_i64 (func (param i64) (result i64))) @@ -3312,6 +3312,20 @@ (i32.const 2) ) ) + (func $sub-neg-one (param $x i32) (param $y i64) + (drop + (i32.add + (local.get $x) + (i32.const 1) + ) + ) + (drop + (i64.add + (local.get $y) + (i64.const 1) + ) + ) + ) (func $pre-combine-or (param $x i32) (param $y i32) (drop (i32.ge_s diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index 6921bdaa53c..18971fb076e 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -3762,6 +3762,16 @@ (i32.const 2) ) ) + (func $sub-neg-one (param $x i32) (param $y i64) + (drop (i32.sub + (local.get $x) + (i32.const -1) + )) + (drop (i64.sub + (local.get $y) + (i64.const -1) + )) + ) (func $pre-combine-or (param $x i32) (param $y i32) (drop (i32.or (i32.gt_s From 45b512893b22dc5ab3a04eb3b1f8b71b9aa2633f Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 23 May 2020 15:20:30 +0300 Subject: [PATCH 05/25] fixes --- src/passes/OptimizeInstructions.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index ee61450458e..1b01f038efe 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1389,10 +1389,10 @@ struct OptimizeInstructions } else if (binary->op == Abstract::getBinary(type, Abstract::Sub)) { // x - (-1) ==> x + 1 Builder builder(*getModule()); - return builder - .makeBinary(Abstract::getBinary(type, Abstract::Add), - binary->left, - builder.makeConst(Literal::makeFromInt32(1, type))); + return builder.makeBinary( + Abstract::getBinary(type, Abstract::Add), + binary->left, + builder.makeConst(Literal::makeFromInt32(1, type))); } else if (binary->op == Abstract::getBinary(type, Abstract::RemS) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { @@ -1402,8 +1402,8 @@ struct OptimizeInstructions // (unsigned)x / -1 ==> x != -1 return Builder(*getModule()) .makeBinary(Abstract::getBinary(type, Abstract::Eq), - binary->right, - binary->left); + binary->left, + binary->right); } else if ((binary->op == Abstract::getBinary(type, Abstract::DivS) || binary->op == Abstract::getBinary(type, Abstract::Mul))) { // (signed)x / -1 ==> 0 - x From d9c38d287c442758edaedf00f082b73265cdd1b7 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 23 May 2020 15:57:34 +0300 Subject: [PATCH 06/25] add case for (unsigned)x > -1 => 0 --- src/passes/OptimizeInstructions.cpp | 5 +- test/emcc_hello_world.fromasm | 92 +++++--------- test/emcc_hello_world.fromasm.clamp | 92 +++++--------- test/emcc_hello_world.fromasm.imprecise | 92 +++++--------- .../inlining-optimizing_optimize-level=3.txt | 112 +++++++----------- .../optimize-instructions_all-features.txt | 20 ++++ .../optimize-instructions_all-features.wast | 18 +++ 7 files changed, 174 insertions(+), 257 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 1b01f038efe..bd25a116caf 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1375,7 +1375,6 @@ struct OptimizeInstructions } } // operations on all 1s - // TODO: shortcut method to create an all-ones? if (right->value == Literal(int32_t(-1)) || right->value == Literal(int64_t(-1))) { if (binary->op == Abstract::getBinary(type, Abstract::And)) { @@ -1393,10 +1392,12 @@ struct OptimizeInstructions Abstract::getBinary(type, Abstract::Add), binary->left, builder.makeConst(Literal::makeFromInt32(1, type))); - } else if (binary->op == Abstract::getBinary(type, Abstract::RemS) && + } else if ((binary->op == Abstract::getBinary(type, Abstract::RemS) || + binary->op == GtUInt32 || binary->op == GtUInt64) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { // (signed)x % -1 ==> 0 + // (unsigned)x > -1 ==> 0 return LiteralUtils::makeZero(type, *getModule()); } else if (binary->op == Abstract::getBinary(type, Abstract::DivU)) { // (unsigned)x / -1 ==> x != -1 diff --git a/test/emcc_hello_world.fromasm b/test/emcc_hello_world.fromasm index 774740dc126..b66b70c560f 100644 --- a/test/emcc_hello_world.fromasm +++ b/test/emcc_hello_world.fromasm @@ -7118,78 +7118,48 @@ ) (func $_fmt_u (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) - (local $4 i32) (if - (i32.or - (i32.and - (i32.eqz - (local.get $1) - ) - (i32.gt_u - (local.get $0) - (i32.const -1) - ) - ) - (i32.gt_u - (local.get $1) - (i32.const 0) - ) + (i32.gt_u + (local.get $1) + (i32.const 0) ) - (local.set $0 - (loop $while-in (result i32) - (i32.store8 - (local.tee $2 - (i32.add - (local.get $2) - (i32.const -1) - ) - ) - (i32.or - (call $___uremdi3 - (local.get $0) - (local.get $1) - (i32.const 10) - ) - (i32.const 48) + (loop $while-in + (i32.store8 + (local.tee $2 + (i32.add + (local.get $2) + (i32.const -1) ) ) - (local.set $3 - (call $___udivdi3 + (i32.or + (call $___uremdi3 (local.get $0) (local.get $1) (i32.const 10) ) + (i32.const 48) ) - (local.set $4 - (global.get $tempRet0) + ) + (local.set $0 + (call $___udivdi3 + (local.get $0) + (local.get $1) + (i32.const 10) ) - (if (result i32) - (i32.or - (i32.and - (i32.eq - (local.get $1) - (i32.const 9) - ) - (i32.gt_u - (local.get $0) - (i32.const -1) - ) - ) - (i32.gt_u - (local.get $1) - (i32.const 9) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $4) - ) - (br $while-in) + ) + (local.set $3 + (global.get $tempRet0) + ) + (if + (i32.gt_u + (local.get $1) + (i32.const 9) + ) + (block + (local.set $1 + (local.get $3) ) - (local.get $3) + (br $while-in) ) ) ) diff --git a/test/emcc_hello_world.fromasm.clamp b/test/emcc_hello_world.fromasm.clamp index fecd5a564e0..d3b16ddc26d 100644 --- a/test/emcc_hello_world.fromasm.clamp +++ b/test/emcc_hello_world.fromasm.clamp @@ -7169,78 +7169,48 @@ ) (func $_fmt_u (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) - (local $4 i32) (if - (i32.or - (i32.and - (i32.eqz - (local.get $1) - ) - (i32.gt_u - (local.get $0) - (i32.const -1) - ) - ) - (i32.gt_u - (local.get $1) - (i32.const 0) - ) + (i32.gt_u + (local.get $1) + (i32.const 0) ) - (local.set $0 - (loop $while-in (result i32) - (i32.store8 - (local.tee $2 - (i32.add - (local.get $2) - (i32.const -1) - ) - ) - (i32.or - (call $___uremdi3 - (local.get $0) - (local.get $1) - (i32.const 10) - ) - (i32.const 48) + (loop $while-in + (i32.store8 + (local.tee $2 + (i32.add + (local.get $2) + (i32.const -1) ) ) - (local.set $3 - (call $___udivdi3 + (i32.or + (call $___uremdi3 (local.get $0) (local.get $1) (i32.const 10) ) + (i32.const 48) ) - (local.set $4 - (global.get $tempRet0) + ) + (local.set $0 + (call $___udivdi3 + (local.get $0) + (local.get $1) + (i32.const 10) ) - (if (result i32) - (i32.or - (i32.and - (i32.eq - (local.get $1) - (i32.const 9) - ) - (i32.gt_u - (local.get $0) - (i32.const -1) - ) - ) - (i32.gt_u - (local.get $1) - (i32.const 9) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $4) - ) - (br $while-in) + ) + (local.set $3 + (global.get $tempRet0) + ) + (if + (i32.gt_u + (local.get $1) + (i32.const 9) + ) + (block + (local.set $1 + (local.get $3) ) - (local.get $3) + (br $while-in) ) ) ) diff --git a/test/emcc_hello_world.fromasm.imprecise b/test/emcc_hello_world.fromasm.imprecise index 4c5d7afd213..ce1db8ea212 100644 --- a/test/emcc_hello_world.fromasm.imprecise +++ b/test/emcc_hello_world.fromasm.imprecise @@ -7014,78 +7014,48 @@ ) (func $_fmt_u (; has Stack IR ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) - (local $4 i32) (if - (i32.or - (i32.and - (i32.eqz - (local.get $1) - ) - (i32.gt_u - (local.get $0) - (i32.const -1) - ) - ) - (i32.gt_u - (local.get $1) - (i32.const 0) - ) + (i32.gt_u + (local.get $1) + (i32.const 0) ) - (local.set $0 - (loop $while-in (result i32) - (i32.store8 - (local.tee $2 - (i32.add - (local.get $2) - (i32.const -1) - ) - ) - (i32.or - (call $___uremdi3 - (local.get $0) - (local.get $1) - (i32.const 10) - ) - (i32.const 48) + (loop $while-in + (i32.store8 + (local.tee $2 + (i32.add + (local.get $2) + (i32.const -1) ) ) - (local.set $3 - (call $___udivdi3 + (i32.or + (call $___uremdi3 (local.get $0) (local.get $1) (i32.const 10) ) + (i32.const 48) ) - (local.set $4 - (global.get $tempRet0) + ) + (local.set $0 + (call $___udivdi3 + (local.get $0) + (local.get $1) + (i32.const 10) ) - (if (result i32) - (i32.or - (i32.and - (i32.eq - (local.get $1) - (i32.const 9) - ) - (i32.gt_u - (local.get $0) - (i32.const -1) - ) - ) - (i32.gt_u - (local.get $1) - (i32.const 9) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $4) - ) - (br $while-in) + ) + (local.set $3 + (global.get $tempRet0) + ) + (if + (i32.gt_u + (local.get $1) + (i32.const 9) + ) + (block + (local.set $1 + (local.get $3) ) - (local.get $3) + (br $while-in) ) ) ) diff --git a/test/passes/inlining-optimizing_optimize-level=3.txt b/test/passes/inlining-optimizing_optimize-level=3.txt index f951319de58..bcb7db0bdd1 100644 --- a/test/passes/inlining-optimizing_optimize-level=3.txt +++ b/test/passes/inlining-optimizing_optimize-level=3.txt @@ -7348,88 +7348,56 @@ ) (func $_fmt_u (param $0 i32) (param $1 i32) (param $2 i32) (result i32) (local $3 i32) - (local $4 i32) (if - (block (result i32) - (if - (i32.or - (i32.and - (i32.eqz - (local.get $1) - ) - (i32.gt_u - (local.get $0) - (i32.const -1) - ) + (i32.gt_u + (local.get $1) + (i32.const 0) + ) + (loop $while-in + (i32.store8 + (local.tee $2 + (i32.add + (local.get $2) + (i32.const -1) ) - (i32.gt_u + ) + (i32.or + (call $___uremdi3 + (local.get $0) (local.get $1) + (i32.const 10) (i32.const 0) ) + (i32.const 48) ) - (local.set $0 - (loop $while-in (result i32) - (i32.store8 - (local.tee $2 - (i32.add - (local.get $2) - (i32.const -1) - ) - ) - (i32.or - (call $___uremdi3 - (local.get $0) - (local.get $1) - (i32.const 10) - (i32.const 0) - ) - (i32.const 48) - ) - ) - (local.set $3 - (call $___udivdi3 - (local.get $0) - (local.get $1) - (i32.const 10) - (i32.const 0) - ) - ) - (local.set $4 - (global.get $tempRet0) - ) - (if (result i32) - (i32.or - (i32.and - (i32.eq - (local.get $1) - (i32.const 9) - ) - (i32.gt_u - (local.get $0) - (i32.const -1) - ) - ) - (i32.gt_u - (local.get $1) - (i32.const 9) - ) - ) - (block - (local.set $0 - (local.get $3) - ) - (local.set $1 - (local.get $4) - ) - (br $while-in) - ) - (local.get $3) - ) + ) + (local.set $0 + (call $___udivdi3 + (local.get $0) + (local.get $1) + (i32.const 10) + (i32.const 0) + ) + ) + (local.set $3 + (global.get $tempRet0) + ) + (if + (i32.gt_u + (local.get $1) + (i32.const 9) + ) + (block + (local.set $1 + (local.get $3) ) + (br $while-in) ) ) - (local.get $0) ) + ) + (if + (local.get $0) (loop $while-in1 (i32.store8 (local.tee $2 diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index 6a0760e7c76..2cbea5acc62 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -3326,6 +3326,26 @@ ) ) ) + (func $greater-than-neg-one (param $x i32) (param $y i64) + (drop + (i32.const 0) + ) + (drop + (i64.const 0) + ) + (drop + (i32.gt_s + (local.get $x) + (i32.const -1) + ) + ) + (drop + (i64.gt_s + (local.get $y) + (i64.const -1) + ) + ) + ) (func $pre-combine-or (param $x i32) (param $y i32) (drop (i32.ge_s diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index 18971fb076e..f92761c8c1f 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -3772,6 +3772,24 @@ (i64.const -1) )) ) + (func $greater-than-neg-one (param $x i32) (param $y i64) + (drop (i32.gt_u + (local.get $x) + (i32.const -1) + )) + (drop (i64.gt_u + (local.get $y) + (i64.const -1) + )) + (drop (i32.gt_s + (local.get $x) + (i32.const -1) + )) + (drop (i64.gt_s + (local.get $y) + (i64.const -1) + )) + ) (func $pre-combine-or (param $x i32) (param $y i32) (drop (i32.or (i32.gt_s From cf911109362298e6e3f1e9c471e606221418889a Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 23 May 2020 16:46:26 +0300 Subject: [PATCH 07/25] switch to templete for pow-of-two methods --- src/passes/OptimizeInstructions.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index bd25a116caf..3468b89e023 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -594,10 +594,12 @@ struct OptimizeInstructions uint64_t c = right->value.geti64(); if (IsPowerOf2(c)) { switch (binary->op) { - // TODO: case MulInt64: + return optimizePowerOf2Mul(binary, c); case RemUInt64: + return optimizePowerOf2URem(binary, c); case DivUInt64: + return optimizePowerOf2UDiv(binary, c); default: break; } @@ -1283,10 +1285,10 @@ struct OptimizeInstructions // but it's still worth doing since // * Often shifts are more common than muls. // * The constant is smaller. - Expression* optimizePowerOf2Mul(Binary* binary, uint32_t c) { - uint32_t shifts = CountTrailingZeroes(c); - binary->op = ShlInt32; - binary->right->cast()->value = Literal(int32_t(shifts)); + template Expression* optimizePowerOf2Mul(Binary* binary, T c) { + int32_t shifts = CountTrailingZeroes(c); + binary->op = sizeof(T) == 4 ? ShlInt32 : ShlInt64; + binary->right->cast()->value = Literal(static_cast(shifts)); return binary; } @@ -1295,10 +1297,10 @@ struct OptimizeInstructions // but it's still worth doing since // * Usually ands are more common than urems. // * The constant is slightly smaller. - Expression* optimizePowerOf2UDiv(Binary* binary, uint32_t c) { - uint32_t shifts = CountTrailingZeroes(c); - binary->op = ShrUInt32; - binary->right->cast()->value = Literal(int32_t(shifts)); + template Expression* optimizePowerOf2UDiv(Binary* binary, T c) { + int32_t shifts = CountTrailingZeroes(c); + binary->op = sizeof(T) == 4 ? ShrUInt32 : ShrUInt64; + binary->right->cast()->value = Literal(static_cast(shifts)); return binary; } @@ -1308,9 +1310,9 @@ struct OptimizeInstructions // but it's still worth doing since // * Usually ands are more common than urems. // * The constant is slightly smaller. - Expression* optimizePowerOf2URem(Binary* binary, uint32_t c) { - binary->op = AndInt32; - binary->right->cast()->value = Literal(int32_t(c - 1)); + template Expression* optimizePowerOf2URem(Binary* binary, T c) { + binary->op = sizeof(T) == 4 ? AndInt32 : AndInt64; + binary->right->cast()->value = Literal(c - 1); return binary; } From 6012b2b05d6083ab8b62664eaf5df0b2099ad065 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 23 May 2020 16:50:53 +0300 Subject: [PATCH 08/25] minor refactoring --- 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 3468b89e023..f9ceeb958c7 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1287,7 +1287,7 @@ struct OptimizeInstructions // * The constant is smaller. template Expression* optimizePowerOf2Mul(Binary* binary, T c) { int32_t shifts = CountTrailingZeroes(c); - binary->op = sizeof(T) == 4 ? ShlInt32 : ShlInt64; + binary->op = sizeof(T) <= 4 ? ShlInt32 : ShlInt64; binary->right->cast()->value = Literal(static_cast(shifts)); return binary; } @@ -1299,7 +1299,7 @@ struct OptimizeInstructions // * The constant is slightly smaller. template Expression* optimizePowerOf2UDiv(Binary* binary, T c) { int32_t shifts = CountTrailingZeroes(c); - binary->op = sizeof(T) == 4 ? ShrUInt32 : ShrUInt64; + binary->op = sizeof(T) <= 4 ? ShrUInt32 : ShrUInt64; binary->right->cast()->value = Literal(static_cast(shifts)); return binary; } @@ -1311,7 +1311,7 @@ struct OptimizeInstructions // * Usually ands are more common than urems. // * The constant is slightly smaller. template Expression* optimizePowerOf2URem(Binary* binary, T c) { - binary->op = sizeof(T) == 4 ? AndInt32 : AndInt64; + binary->op = sizeof(T) <= 4 ? AndInt32 : AndInt64; binary->right->cast()->value = Literal(c - 1); return binary; } From bc8c5537bacc607d57038ab1e17aed8fcd05a156 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 23 May 2020 20:07:53 +0300 Subject: [PATCH 09/25] Add cases for x <= -1 for signed and unsigned cases --- auto_update_tests.py | 2 +- src/passes/OptimizeInstructions.cpp | 12 ++++++++++ test/emcc_O2_hello_world.fromasm | 4 ++-- test/emcc_O2_hello_world.fromasm.clamp | 4 ++-- test/emcc_O2_hello_world.fromasm.imprecise | 4 ++-- test/emcc_hello_world.fromasm | 4 ++-- test/emcc_hello_world.fromasm.clamp | 4 ++-- test/emcc_hello_world.fromasm.imprecise | 4 ++-- test/memorygrowth.fromasm | 4 ++-- test/memorygrowth.fromasm.clamp | 4 ++-- test/memorygrowth.fromasm.imprecise | 4 ++-- .../inlining-optimizing_optimize-level=3.txt | 4 ++-- .../optimize-instructions_all-features.txt | 20 +++++++++++++++++ .../optimize-instructions_all-features.wast | 22 ++++++++++++++++++- 14 files changed, 74 insertions(+), 22 deletions(-) diff --git a/auto_update_tests.py b/auto_update_tests.py index 5cfff9c0ed3..faa61e4c4df 100755 --- a/auto_update_tests.py +++ b/auto_update_tests.py @@ -230,7 +230,7 @@ def update_spec_tests(): ('wasm-opt', wasm_opt.update_wasm_opt_tests), ('asm2wasm', update_asm_js_tests), ('wasm-dis', update_wasm_dis_tests), - ('example', update_example_tests), + # ('example', update_example_tests), ('ctor-eval', update_ctor_eval_tests), ('wasm-metadce', update_metadce_tests), ('wasm-reduce', update_reduce_tests), diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index f9ceeb958c7..b91cf689c00 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1416,6 +1416,18 @@ struct OptimizeInstructions Abstract::getBinary(type, Abstract::Sub), builder.makeConst(Literal::makeFromInt32(0, type)), binary->left); + } else if ((binary->op == LeUInt32 || binary->op == LeUInt64) && + !EffectAnalyzer(getPassOptions(), features, binary->left) + .hasSideEffects()) { + // (unsigned)x <= -1 ==> 1 + return LiteralUtils::makeFromInt32(1, type, *getModule()); + } else if (binary->op == LeSInt32 || binary->op == LeSInt64) { + // (signed)x <= -1 ==> (unsigned)x >> sizeof(bits) - 1 + Builder builder(*getModule()); + return builder.makeBinary(Abstract::getBinary(type, Abstract::ShrU), + binary->left, + builder.makeConst(Literal::makeFromInt32( + type.getByteSize() * 8 - 1, type))); } } // wasm binary encoding uses signed LEBs, which slightly favor negative diff --git a/test/emcc_O2_hello_world.fromasm b/test/emcc_O2_hello_world.fromasm index bd9af676bc7..33b0d77d92e 100644 --- a/test/emcc_O2_hello_world.fromasm +++ b/test/emcc_O2_hello_world.fromasm @@ -7871,11 +7871,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.le_s + (i32.shr_u (i32.load offset=76 (local.get $0) ) - (i32.const -1) + (i32.const 31) ) (br $do-once (call $___fflush_unlocked diff --git a/test/emcc_O2_hello_world.fromasm.clamp b/test/emcc_O2_hello_world.fromasm.clamp index bd9af676bc7..33b0d77d92e 100644 --- a/test/emcc_O2_hello_world.fromasm.clamp +++ b/test/emcc_O2_hello_world.fromasm.clamp @@ -7871,11 +7871,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.le_s + (i32.shr_u (i32.load offset=76 (local.get $0) ) - (i32.const -1) + (i32.const 31) ) (br $do-once (call $___fflush_unlocked diff --git a/test/emcc_O2_hello_world.fromasm.imprecise b/test/emcc_O2_hello_world.fromasm.imprecise index 82edc64b961..bc84cee256d 100644 --- a/test/emcc_O2_hello_world.fromasm.imprecise +++ b/test/emcc_O2_hello_world.fromasm.imprecise @@ -7868,11 +7868,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.le_s + (i32.shr_u (i32.load offset=76 (local.get $0) ) - (i32.const -1) + (i32.const 31) ) (br $do-once (call $___fflush_unlocked diff --git a/test/emcc_hello_world.fromasm b/test/emcc_hello_world.fromasm index b66b70c560f..a84af0e37fd 100644 --- a/test/emcc_hello_world.fromasm +++ b/test/emcc_hello_world.fromasm @@ -538,11 +538,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.le_s + (i32.shr_u (i32.load offset=76 (local.get $0) ) - (i32.const -1) + (i32.const 31) ) (br $do-once (call $___fflush_unlocked diff --git a/test/emcc_hello_world.fromasm.clamp b/test/emcc_hello_world.fromasm.clamp index d3b16ddc26d..0d12a6c7e21 100644 --- a/test/emcc_hello_world.fromasm.clamp +++ b/test/emcc_hello_world.fromasm.clamp @@ -537,11 +537,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.le_s + (i32.shr_u (i32.load offset=76 (local.get $0) ) - (i32.const -1) + (i32.const 31) ) (br $do-once (call $___fflush_unlocked diff --git a/test/emcc_hello_world.fromasm.imprecise b/test/emcc_hello_world.fromasm.imprecise index ce1db8ea212..068bb5fc7bc 100644 --- a/test/emcc_hello_world.fromasm.imprecise +++ b/test/emcc_hello_world.fromasm.imprecise @@ -535,11 +535,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.le_s + (i32.shr_u (i32.load offset=76 (local.get $0) ) - (i32.const -1) + (i32.const 31) ) (br $do-once (call $___fflush_unlocked diff --git a/test/memorygrowth.fromasm b/test/memorygrowth.fromasm index 9c909afcf14..15ce9495bb2 100644 --- a/test/memorygrowth.fromasm +++ b/test/memorygrowth.fromasm @@ -7991,11 +7991,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.le_s + (i32.shr_u (i32.load offset=76 (local.get $0) ) - (i32.const -1) + (i32.const 31) ) (br $do-once (call $$a diff --git a/test/memorygrowth.fromasm.clamp b/test/memorygrowth.fromasm.clamp index 9c909afcf14..15ce9495bb2 100644 --- a/test/memorygrowth.fromasm.clamp +++ b/test/memorygrowth.fromasm.clamp @@ -7991,11 +7991,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.le_s + (i32.shr_u (i32.load offset=76 (local.get $0) ) - (i32.const -1) + (i32.const 31) ) (br $do-once (call $$a diff --git a/test/memorygrowth.fromasm.imprecise b/test/memorygrowth.fromasm.imprecise index b90edfcd6ac..b65485e30f4 100644 --- a/test/memorygrowth.fromasm.imprecise +++ b/test/memorygrowth.fromasm.imprecise @@ -7985,11 +7985,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.le_s + (i32.shr_u (i32.load offset=76 (local.get $0) ) - (i32.const -1) + (i32.const 31) ) (br $do-once (call $$a diff --git a/test/passes/inlining-optimizing_optimize-level=3.txt b/test/passes/inlining-optimizing_optimize-level=3.txt index bcb7db0bdd1..751352bef80 100644 --- a/test/passes/inlining-optimizing_optimize-level=3.txt +++ b/test/passes/inlining-optimizing_optimize-level=3.txt @@ -557,11 +557,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.le_s + (i32.shr_u (i32.load offset=76 (local.get $0) ) - (i32.const -1) + (i32.const 31) ) (br $do-once (call $___fflush_unlocked diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index 2cbea5acc62..d1b7daceef7 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -3346,6 +3346,26 @@ ) ) ) + (func $less-on-equal-than-neg-one (param $x i32) (param $y i64) + (drop + (i32.const 1) + ) + (drop + (i64.const 1) + ) + (drop + (i32.shr_u + (local.get $x) + (i32.const 31) + ) + ) + (drop + (i64.shr_u + (local.get $y) + (i64.const 63) + ) + ) + ) (func $pre-combine-or (param $x i32) (param $y i32) (drop (i32.ge_s diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index f92761c8c1f..41961202a3b 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -3781,7 +3781,7 @@ (local.get $y) (i64.const -1) )) - (drop (i32.gt_s + (drop (i32.gt_s (local.get $x) (i32.const -1) )) @@ -3790,6 +3790,26 @@ (i64.const -1) )) ) + (func $less-on-equal-than-neg-one (param $x i32) (param $y i64) + ;; (unsigned)x <= -1 ==> 1 + (drop (i32.le_u + (local.get $x) + (i32.const -1) + )) + (drop (i64.le_u + (local.get $y) + (i64.const -1) + )) + ;; (signed)x <= -1 ==> (unsigned)x >> sizeof(bits) - 1 + (drop (i32.le_s + (local.get $x) + (i32.const -1) + )) + (drop (i64.le_s + (local.get $y) + (i64.const -1) + )) + ) (func $pre-combine-or (param $x i32) (param $y i32) (drop (i32.or (i32.gt_s From 1e246b604e78b825826c79c05fba56749847abcf Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 23 May 2020 20:16:17 +0300 Subject: [PATCH 10/25] restore commented test --- auto_update_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto_update_tests.py b/auto_update_tests.py index faa61e4c4df..5cfff9c0ed3 100755 --- a/auto_update_tests.py +++ b/auto_update_tests.py @@ -230,7 +230,7 @@ def update_spec_tests(): ('wasm-opt', wasm_opt.update_wasm_opt_tests), ('asm2wasm', update_asm_js_tests), ('wasm-dis', update_wasm_dis_tests), - # ('example', update_example_tests), + ('example', update_example_tests), ('ctor-eval', update_ctor_eval_tests), ('wasm-metadce', update_metadce_tests), ('wasm-reduce', update_reduce_tests), From 10cf654ff1dd7a0f5bdee89786caef6e5fa73179 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sun, 24 May 2020 16:47:03 +0300 Subject: [PATCH 11/25] add tests for 64-bit mul with pow of two const --- .../optimize-instructions_all-features.txt | 89 ++++++++++++++---- .../optimize-instructions_all-features.wast | 91 +++++++++++++++---- 2 files changed, 147 insertions(+), 33 deletions(-) diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index d1b7daceef7..a614108e7e4 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -7,8 +7,8 @@ (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) (type $i32_=>_none (func (param i32))) (type $none_=>_i64 (func (result i64))) - (type $i32_i64_f32_=>_none (func (param i32 i64 f32))) (type $i64_=>_i64 (func (param i64) (result i64))) + (type $i32_i64_f32_=>_none (func (param i32 i64 f32))) (type $i32_i32_f64_f64_=>_none (func (param i32 i32 f64 f64))) (type $i32_i64_f32_f64_=>_none (func (param i32 i64 f32 f64))) (type $i32_i64_f64_i32_=>_none (func (param i32 i64 f64 i32))) @@ -2521,9 +2521,9 @@ ) ) ) - (func $mul-power-2 (param $x i32) (result i32) + (func $mul-32-power-2 (param $x i32) (result i32) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.shl (local.get $x) (i32.const 2) @@ -2531,7 +2531,7 @@ ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.mul (local.get $x) (i32.const 5) @@ -2539,19 +2539,19 @@ ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (local.get $x) ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.const 0) ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.mul - (call $mul-power-2 + (call $mul-32-power-2 (i32.const 123) ) (i32.const 0) @@ -2559,7 +2559,7 @@ ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.sub (i32.const 0) (local.get $x) @@ -2567,7 +2567,7 @@ ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.shl (local.get $x) (i32.const 31) @@ -2576,6 +2576,61 @@ ) (unreachable) ) + (func $mul-64-power-2 (param $x i64) (result i64) + (drop + (call $mul-64-power-2 + (i64.shl + (local.get $x) + (i64.const 2) + ) + ) + ) + (drop + (call $mul-64-power-2 + (i64.mul + (local.get $x) + (i64.const 5) + ) + ) + ) + (drop + (call $mul-64-power-2 + (local.get $x) + ) + ) + (drop + (call $mul-64-power-2 + (i64.const 0) + ) + ) + (drop + (call $mul-64-power-2 + (i64.mul + (call $mul-64-power-2 + (i64.const 123) + ) + (i64.const 0) + ) + ) + ) + (drop + (call $mul-64-power-2 + (i64.sub + (i64.const 0) + (local.get $x) + ) + ) + ) + (drop + (call $mul-64-power-2 + (i64.shl + (local.get $x) + (i64.const 63) + ) + ) + ) + (unreachable) + ) (func $div-32-power-2 (param $x i32) (result i32) (drop (call $div-32-power-2 @@ -2634,9 +2689,9 @@ ) (unreachable) ) - (func $urem-power-2 (param $x i32) (result i32) + (func $urem-32-power-2 (param $x i32) (result i32) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.and (local.get $x) (i32.const 3) @@ -2644,7 +2699,7 @@ ) ) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.rem_u (local.get $x) (i32.const 5) @@ -2652,12 +2707,12 @@ ) ) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.const 0) ) ) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.rem_u (local.get $x) (i32.const 0) @@ -2665,7 +2720,7 @@ ) ) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.rem_u (local.get $x) (i32.const -1) @@ -2673,7 +2728,7 @@ ) ) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.and (local.get $x) (i32.const 2147483647) diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index 41961202a3b..c406d3b5c13 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -2856,9 +2856,9 @@ ) ) ) - (func $mul-power-2 (param $x i32) (result i32) + (func $mul-32-power-2 (param $x i32) (result i32) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.mul (local.get $x) (i32.const 4) @@ -2866,7 +2866,7 @@ ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.mul (local.get $x) (i32.const 5) @@ -2874,7 +2874,7 @@ ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.mul (local.get $x) (i32.const 1) @@ -2882,7 +2882,7 @@ ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.mul (local.get $x) (i32.const 0) @@ -2890,15 +2890,15 @@ ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.mul - (call $mul-power-2 (i32.const 123)) ;; side effects + (call $mul-32-power-2 (i32.const 123)) ;; side effects (i32.const 0) ) ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.mul (local.get $x) (i32.const 0xffffffff) @@ -2906,7 +2906,7 @@ ) ) (drop - (call $mul-power-2 + (call $mul-32-power-2 (i32.mul (local.get $x) (i32.const 0x80000000) @@ -2914,6 +2914,65 @@ ) ) (unreachable) + ) + (func $mul-64-power-2 (param $x i64) (result i64) + (drop + (call $mul-64-power-2 + (i64.mul + (local.get $x) + (i64.const 4) + ) + ) + ) + (drop + (call $mul-64-power-2 + (i64.mul + (local.get $x) + (i64.const 5) + ) + ) + ) + (drop + (call $mul-64-power-2 + (i64.mul + (local.get $x) + (i64.const 1) + ) + ) + ) + (drop + (call $mul-64-power-2 + (i64.mul + (local.get $x) + (i64.const 0) + ) + ) + ) + (drop + (call $mul-64-power-2 + (i64.mul + (call $mul-64-power-2 (i64.const 123)) ;; side effects + (i64.const 0) + ) + ) + ) + (drop + (call $mul-64-power-2 + (i64.mul + (local.get $x) + (i64.const 0xffffffffffffffff) + ) + ) + ) + (drop + (call $mul-64-power-2 + (i64.mul + (local.get $x) + (i64.const 0x8000000000000000) + ) + ) + ) + (unreachable) ) (func $div-32-power-2 (param $x i32) (result i32) (drop @@ -2974,9 +3033,9 @@ ) (unreachable) ) - (func $urem-power-2 (param $x i32) (result i32) + (func $urem-32-power-2 (param $x i32) (result i32) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.rem_u (local.get $x) (i32.const 4) @@ -2984,7 +3043,7 @@ ) ) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.rem_u (local.get $x) (i32.const 5) @@ -2992,7 +3051,7 @@ ) ) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.rem_u (local.get $x) (i32.const 1) @@ -3000,7 +3059,7 @@ ) ) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.rem_u (local.get $x) (i32.const 0) @@ -3008,7 +3067,7 @@ ) ) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.rem_u (local.get $x) (i32.const 0xffffffff) @@ -3016,7 +3075,7 @@ ) ) (drop - (call $urem-power-2 + (call $urem-32-power-2 (i32.rem_u (local.get $x) (i32.const 0x80000000) From f29ee7b5c4bd40a1e03afa4d6b639d4805c91fea Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sun, 24 May 2020 18:45:48 +0300 Subject: [PATCH 12/25] remove x / -1 transform --- src/passes/OptimizeInstructions.cpp | 6 ++---- test/unit.fromasm.imprecise | 5 ++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index b91cf689c00..6a5e84f0a0d 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1407,10 +1407,8 @@ struct OptimizeInstructions .makeBinary(Abstract::getBinary(type, Abstract::Eq), binary->left, binary->right); - } else if ((binary->op == Abstract::getBinary(type, Abstract::DivS) || - binary->op == Abstract::getBinary(type, Abstract::Mul))) { - // (signed)x / -1 ==> 0 - x - // (signed)x * -1 ==> 0 - x + } else if (binary->op == Abstract::getBinary(type, Abstract::Mul)) { + // (signed)x * -1 ==> -x Builder builder(*getModule()); return builder.makeBinary( Abstract::getBinary(type, Abstract::Sub), diff --git a/test/unit.fromasm.imprecise b/test/unit.fromasm.imprecise index 48cef072918..afbaea82afc 100644 --- a/test/unit.fromasm.imprecise +++ b/test/unit.fromasm.imprecise @@ -220,7 +220,10 @@ (i32.const 2147483647) ) (func $trapping_sint_div_s (; has Stack IR ;) (result i32) - (i32.const -2147483648) + (i32.div_s + (i32.const -2147483648) + (i32.const -1) + ) ) (func $fr (; has Stack IR ;) (param $0 f32) (nop) From c7f3f804ff1bce62562eef8f2a23a2116f4adb7f Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sun, 24 May 2020 21:20:00 +0300 Subject: [PATCH 13/25] add (unsigned)x < -1. Fix comparision --- src/passes/OptimizeInstructions.cpp | 8 +++++--- test/passes/optimize-instructions_all-features.txt | 2 +- test/wasm2js/i64-ctz.2asm.js.opt | 2 +- test/wasm2js/unary-ops.2asm.js.opt | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 6a5e84f0a0d..efabc9fab18 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1398,13 +1398,15 @@ struct OptimizeInstructions binary->op == GtUInt32 || binary->op == GtUInt64) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { - // (signed)x % -1 ==> 0 + // (signed)x % -1 ==> 0 // (unsigned)x > -1 ==> 0 return LiteralUtils::makeZero(type, *getModule()); - } else if (binary->op == Abstract::getBinary(type, Abstract::DivU)) { + } else if (binary->op == LtUInt32 || binary->op == LtUInt64 || + binary->op == Abstract::getBinary(type, Abstract::DivU)) { // (unsigned)x / -1 ==> x != -1 + // (unsigned)x < -1 ==> x != -1 return Builder(*getModule()) - .makeBinary(Abstract::getBinary(type, Abstract::Eq), + .makeBinary(Abstract::getBinary(type, Abstract::Ne), binary->left, binary->right); } else if (binary->op == Abstract::getBinary(type, Abstract::Mul)) { diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index a614108e7e4..5cbaa8cb931 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -2673,7 +2673,7 @@ ) (drop (call $div-32-power-2 - (i32.eq + (i32.ne (local.get $x) (i32.const -1) ) diff --git a/test/wasm2js/i64-ctz.2asm.js.opt b/test/wasm2js/i64-ctz.2asm.js.opt index 6e1a54fae58..b651dae2908 100644 --- a/test/wasm2js/i64-ctz.2asm.js.opt +++ b/test/wasm2js/i64-ctz.2asm.js.opt @@ -40,7 +40,7 @@ function asmFunc(global, env, buffer) { if ($0 | $1) { $3 = $1 + -1 | 0; $2 = $0 + -1 | 0; - if ($2 >>> 0 < 4294967295) { + if (($2 | 0) != -1) { $3 = $3 + 1 | 0 } $2 = Math_clz32($0 ^ $2) + 32 | 0; diff --git a/test/wasm2js/unary-ops.2asm.js.opt b/test/wasm2js/unary-ops.2asm.js.opt index 180f89dd0d1..425e223ffe9 100644 --- a/test/wasm2js/unary-ops.2asm.js.opt +++ b/test/wasm2js/unary-ops.2asm.js.opt @@ -81,7 +81,7 @@ function asmFunc(global, env, buffer) { if ($0 | $1_1) { $3 = $1_1 + -1 | 0; $2 = $0 + -1 | 0; - if ($2 >>> 0 < 4294967295) { + if (($2 | 0) != -1) { $3 = $3 + 1 | 0 } $2 = Math_clz32($0 ^ $2) + 32 | 0; From 6016fa9287dc92a0b07abe7b8782ab2403e1a548 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 26 May 2020 18:06:28 +0300 Subject: [PATCH 14/25] fix binaryen_js tests --- test/binaryen.js/sieve.js.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/binaryen.js/sieve.js.txt b/test/binaryen.js/sieve.js.txt index 7a4223cd853..1da65a09f15 100644 --- a/test/binaryen.js/sieve.js.txt +++ b/test/binaryen.js/sieve.js.txt @@ -73,12 +73,12 @@ optimized: (drop (memory.grow (i32.sub - (i32.div_u + (i32.shr_u (i32.add (local.get $0) (i32.const 65535) ) - (i32.const 65536) + (i32.const 16) ) (memory.size) ) From 71f8991071586bc6391059ecff98c8d953161d50 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 28 May 2020 11:39:24 +0300 Subject: [PATCH 15/25] refactor & fix according review --- src/ir/abstract.h | 40 ++++++++++ src/passes/OptimizeInstructions.cpp | 78 +++++++++---------- test/emcc_O2_hello_world.fromasm | 4 +- test/emcc_O2_hello_world.fromasm.clamp | 4 +- test/emcc_O2_hello_world.fromasm.imprecise | 4 +- test/emcc_hello_world.fromasm | 4 +- test/emcc_hello_world.fromasm.clamp | 4 +- test/emcc_hello_world.fromasm.imprecise | 4 +- test/memorygrowth.fromasm | 4 +- test/memorygrowth.fromasm.clamp | 4 +- test/memorygrowth.fromasm.imprecise | 4 +- .../inlining-optimizing_optimize-level=3.txt | 4 +- .../optimize-instructions_all-features.txt | 10 +-- 13 files changed, 102 insertions(+), 66 deletions(-) diff --git a/src/ir/abstract.h b/src/ir/abstract.h index f7bc664dd29..ea3bc22c688 100644 --- a/src/ir/abstract.h +++ b/src/ir/abstract.h @@ -46,6 +46,14 @@ enum Op { // Relational Eq, Ne, + LtS, + LtU, + LeS, + LeU, + GtS, + GtU, + GeS, + GeU }; // Provide a wasm type and an abstract op and get the concrete one. For example, @@ -126,6 +134,22 @@ inline BinaryOp getBinary(Type type, Op op) { return EqInt32; case Ne: return NeInt32; + case LtS: + return LtSInt32; + case LtU: + return LtUInt32; + case LeS: + return LeSInt32; + case LeU: + return LeUInt32; + case GtS: + return GtSInt32; + case GtU: + return GtUInt32; + case GeS: + return GeSInt32; + case GeU: + return GeUInt32; default: return InvalidBinary; } @@ -163,6 +187,22 @@ inline BinaryOp getBinary(Type type, Op op) { return EqInt64; case Ne: return NeInt64; + case LtS: + return LtSInt64; + case LtU: + return LtUInt64; + case LeS: + return LeSInt64; + case LeU: + return LeUInt64; + case GtS: + return GtSInt64; + case GtU: + return GtUInt64; + case GeS: + return GeSInt64; + case GeU: + return GeUInt64; default: return InvalidBinary; } diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index efabc9fab18..c5cc3a7517b 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -19,6 +19,7 @@ // #include +#include #include #include @@ -1286,32 +1287,35 @@ struct OptimizeInstructions // * Often shifts are more common than muls. // * The constant is smaller. template Expression* optimizePowerOf2Mul(Binary* binary, T c) { - int32_t shifts = CountTrailingZeroes(c); - binary->op = sizeof(T) <= 4 ? ShlInt32 : ShlInt64; + static_assert(std::is_same::value || + std::is_same::value, + "type mismatch"); + auto shifts = CountTrailingZeroes(c); + binary->op = std::is_same::value ? ShlInt32 : ShlInt64; binary->right->cast()->value = Literal(static_cast(shifts)); return binary; } - // Optimize an unsigned divide by a power of two on the right + // Optimize an unsigned divide / remainder by a power of two on the right // This doesn't shrink code size, and VMs likely optimize it anyhow, // but it's still worth doing since // * Usually ands are more common than urems. // * The constant is slightly smaller. template Expression* optimizePowerOf2UDiv(Binary* binary, T c) { - int32_t shifts = CountTrailingZeroes(c); - binary->op = sizeof(T) <= 4 ? ShrUInt32 : ShrUInt64; + static_assert(std::is_same::value || + std::is_same::value, + "type mismatch"); + auto shifts = CountTrailingZeroes(c); + binary->op = std::is_same::value ? ShrUInt32 : ShrUInt64; binary->right->cast()->value = Literal(static_cast(shifts)); return binary; } - // Optimize an unsigned divide by a power of two on the right, - // which can be an AND mask - // This doesn't shrink code size, and VMs likely optimize it anyhow, - // but it's still worth doing since - // * Usually ands are more common than urems. - // * The constant is slightly smaller. template Expression* optimizePowerOf2URem(Binary* binary, T c) { - binary->op = sizeof(T) <= 4 ? AndInt32 : AndInt64; + static_assert(std::is_same::value || + std::is_same::value, + "type mismatch"); + binary->op = std::is_same::value ? AndInt32 : AndInt64; binary->right->cast()->value = Literal(c - 1); return binary; } @@ -1385,49 +1389,41 @@ struct OptimizeInstructions } else if (binary->op == Abstract::getBinary(type, Abstract::Or) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { - // x & -1 ==> -1 + // x | -1 ==> -1 return binary->right; } else if (binary->op == Abstract::getBinary(type, Abstract::Sub)) { // x - (-1) ==> x + 1 - Builder builder(*getModule()); - return builder.makeBinary( - Abstract::getBinary(type, Abstract::Add), - binary->left, - builder.makeConst(Literal::makeFromInt32(1, type))); + binary->op = Abstract::getBinary(type, Abstract::Add); + right->value = Literal::makeFromInt32(1, type); + return binary; } else if ((binary->op == Abstract::getBinary(type, Abstract::RemS) || - binary->op == GtUInt32 || binary->op == GtUInt64) && + binary->op == Abstract::getBinary(type, Abstract::GtU)) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { // (signed)x % -1 ==> 0 // (unsigned)x > -1 ==> 0 - return LiteralUtils::makeZero(type, *getModule()); - } else if (binary->op == LtUInt32 || binary->op == LtUInt64 || - binary->op == Abstract::getBinary(type, Abstract::DivU)) { - // (unsigned)x / -1 ==> x != -1 + right->value = Literal::makeSingleZero(type); + return right; + } else if (binary->op == Abstract::getBinary(type, Abstract::LtU)) { // (unsigned)x < -1 ==> x != -1 - return Builder(*getModule()) - .makeBinary(Abstract::getBinary(type, Abstract::Ne), - binary->left, - binary->right); + binary->op = Abstract::getBinary(type, Abstract::Ne); + return binary; + } else if (binary->op == Abstract::getBinary(type, Abstract::DivU)) { + // (unsigned)x / -1 ==> x == -1 + binary->op = Abstract::getBinary(type, Abstract::Eq); + return binary; } else if (binary->op == Abstract::getBinary(type, Abstract::Mul)) { // (signed)x * -1 ==> -x - Builder builder(*getModule()); - return builder.makeBinary( - Abstract::getBinary(type, Abstract::Sub), - builder.makeConst(Literal::makeFromInt32(0, type)), - binary->left); - } else if ((binary->op == LeUInt32 || binary->op == LeUInt64) && + binary->op = Abstract::getBinary(type, Abstract::Sub); + right->value = Literal::makeSingleZero(type); + std::swap(binary->left, binary->right); + return binary; + } else if (binary->op == Abstract::getBinary(type, Abstract::LeU) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { // (unsigned)x <= -1 ==> 1 - return LiteralUtils::makeFromInt32(1, type, *getModule()); - } else if (binary->op == LeSInt32 || binary->op == LeSInt64) { - // (signed)x <= -1 ==> (unsigned)x >> sizeof(bits) - 1 - Builder builder(*getModule()); - return builder.makeBinary(Abstract::getBinary(type, Abstract::ShrU), - binary->left, - builder.makeConst(Literal::makeFromInt32( - type.getByteSize() * 8 - 1, type))); + right->value = Literal::makeFromInt32(1, type); + return right; } } // wasm binary encoding uses signed LEBs, which slightly favor negative diff --git a/test/emcc_O2_hello_world.fromasm b/test/emcc_O2_hello_world.fromasm index 33b0d77d92e..bd9af676bc7 100644 --- a/test/emcc_O2_hello_world.fromasm +++ b/test/emcc_O2_hello_world.fromasm @@ -7871,11 +7871,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.shr_u + (i32.le_s (i32.load offset=76 (local.get $0) ) - (i32.const 31) + (i32.const -1) ) (br $do-once (call $___fflush_unlocked diff --git a/test/emcc_O2_hello_world.fromasm.clamp b/test/emcc_O2_hello_world.fromasm.clamp index 33b0d77d92e..bd9af676bc7 100644 --- a/test/emcc_O2_hello_world.fromasm.clamp +++ b/test/emcc_O2_hello_world.fromasm.clamp @@ -7871,11 +7871,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.shr_u + (i32.le_s (i32.load offset=76 (local.get $0) ) - (i32.const 31) + (i32.const -1) ) (br $do-once (call $___fflush_unlocked diff --git a/test/emcc_O2_hello_world.fromasm.imprecise b/test/emcc_O2_hello_world.fromasm.imprecise index bc84cee256d..82edc64b961 100644 --- a/test/emcc_O2_hello_world.fromasm.imprecise +++ b/test/emcc_O2_hello_world.fromasm.imprecise @@ -7868,11 +7868,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.shr_u + (i32.le_s (i32.load offset=76 (local.get $0) ) - (i32.const 31) + (i32.const -1) ) (br $do-once (call $___fflush_unlocked diff --git a/test/emcc_hello_world.fromasm b/test/emcc_hello_world.fromasm index a84af0e37fd..b66b70c560f 100644 --- a/test/emcc_hello_world.fromasm +++ b/test/emcc_hello_world.fromasm @@ -538,11 +538,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.shr_u + (i32.le_s (i32.load offset=76 (local.get $0) ) - (i32.const 31) + (i32.const -1) ) (br $do-once (call $___fflush_unlocked diff --git a/test/emcc_hello_world.fromasm.clamp b/test/emcc_hello_world.fromasm.clamp index 0d12a6c7e21..d3b16ddc26d 100644 --- a/test/emcc_hello_world.fromasm.clamp +++ b/test/emcc_hello_world.fromasm.clamp @@ -537,11 +537,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.shr_u + (i32.le_s (i32.load offset=76 (local.get $0) ) - (i32.const 31) + (i32.const -1) ) (br $do-once (call $___fflush_unlocked diff --git a/test/emcc_hello_world.fromasm.imprecise b/test/emcc_hello_world.fromasm.imprecise index 068bb5fc7bc..ce1db8ea212 100644 --- a/test/emcc_hello_world.fromasm.imprecise +++ b/test/emcc_hello_world.fromasm.imprecise @@ -535,11 +535,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.shr_u + (i32.le_s (i32.load offset=76 (local.get $0) ) - (i32.const 31) + (i32.const -1) ) (br $do-once (call $___fflush_unlocked diff --git a/test/memorygrowth.fromasm b/test/memorygrowth.fromasm index 15ce9495bb2..9c909afcf14 100644 --- a/test/memorygrowth.fromasm +++ b/test/memorygrowth.fromasm @@ -7991,11 +7991,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.shr_u + (i32.le_s (i32.load offset=76 (local.get $0) ) - (i32.const 31) + (i32.const -1) ) (br $do-once (call $$a diff --git a/test/memorygrowth.fromasm.clamp b/test/memorygrowth.fromasm.clamp index 15ce9495bb2..9c909afcf14 100644 --- a/test/memorygrowth.fromasm.clamp +++ b/test/memorygrowth.fromasm.clamp @@ -7991,11 +7991,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.shr_u + (i32.le_s (i32.load offset=76 (local.get $0) ) - (i32.const 31) + (i32.const -1) ) (br $do-once (call $$a diff --git a/test/memorygrowth.fromasm.imprecise b/test/memorygrowth.fromasm.imprecise index b65485e30f4..b90edfcd6ac 100644 --- a/test/memorygrowth.fromasm.imprecise +++ b/test/memorygrowth.fromasm.imprecise @@ -7985,11 +7985,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.shr_u + (i32.le_s (i32.load offset=76 (local.get $0) ) - (i32.const 31) + (i32.const -1) ) (br $do-once (call $$a diff --git a/test/passes/inlining-optimizing_optimize-level=3.txt b/test/passes/inlining-optimizing_optimize-level=3.txt index 751352bef80..bcb7db0bdd1 100644 --- a/test/passes/inlining-optimizing_optimize-level=3.txt +++ b/test/passes/inlining-optimizing_optimize-level=3.txt @@ -557,11 +557,11 @@ (local.set $0 (block $do-once (result i32) (if - (i32.shr_u + (i32.le_s (i32.load offset=76 (local.get $0) ) - (i32.const 31) + (i32.const -1) ) (br $do-once (call $___fflush_unlocked diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index 5cbaa8cb931..06e54dd29b3 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -2673,7 +2673,7 @@ ) (drop (call $div-32-power-2 - (i32.ne + (i32.eq (local.get $x) (i32.const -1) ) @@ -3409,15 +3409,15 @@ (i64.const 1) ) (drop - (i32.shr_u + (i32.le_s (local.get $x) - (i32.const 31) + (i32.const -1) ) ) (drop - (i64.shr_u + (i64.le_s (local.get $y) - (i64.const 63) + (i64.const -1) ) ) ) From d91d4965b6f3f6b63ba3b73a0e488af2d984120c Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 28 May 2020 22:39:05 +0300 Subject: [PATCH 16/25] fix bugs found by fuzzer --- src/passes/OptimizeInstructions.cpp | 14 ++++++++++---- test/passes/optimize-instructions_all-features.txt | 9 +++++++-- .../passes/optimize-instructions_all-features.wast | 6 ++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index c5cc3a7517b..45ba6041201 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1396,14 +1396,19 @@ struct OptimizeInstructions binary->op = Abstract::getBinary(type, Abstract::Add); right->value = Literal::makeFromInt32(1, type); return binary; - } else if ((binary->op == Abstract::getBinary(type, Abstract::RemS) || - binary->op == Abstract::getBinary(type, Abstract::GtU)) && + } else if (binary->op == Abstract::getBinary(type, Abstract::RemS) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { // (signed)x % -1 ==> 0 - // (unsigned)x > -1 ==> 0 right->value = Literal::makeSingleZero(type); return right; + } else if (binary->op == Abstract::getBinary(type, Abstract::GtU) && + !EffectAnalyzer(getPassOptions(), features, binary->left) + .hasSideEffects()) { + // (unsigned)x > -1 ==> 0 + right->value = Literal::makeSingleZero(Type::i32); + right->finalize(); + return right; } else if (binary->op == Abstract::getBinary(type, Abstract::LtU)) { // (unsigned)x < -1 ==> x != -1 binary->op = Abstract::getBinary(type, Abstract::Ne); @@ -1422,7 +1427,8 @@ struct OptimizeInstructions !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { // (unsigned)x <= -1 ==> 1 - right->value = Literal::makeFromInt32(1, type); + right->value = Literal::makeFromInt32(1, Type::i32); + right->finalize(); return right; } } diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index 06e54dd29b3..903243eb290 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -3386,7 +3386,7 @@ (i32.const 0) ) (drop - (i64.const 0) + (i32.const 0) ) (drop (i32.gt_s @@ -3400,13 +3400,18 @@ (i64.const -1) ) ) + (drop + (i64.extend_i32_s + (i32.const 0) + ) + ) ) (func $less-on-equal-than-neg-one (param $x i32) (param $y i64) (drop (i32.const 1) ) (drop - (i64.const 1) + (i32.const 1) ) (drop (i32.le_s diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index c406d3b5c13..c2417b97ee5 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -3848,6 +3848,12 @@ (local.get $y) (i64.const -1) )) + (drop (i64.extend_i32_s + (i64.gt_u + (i64.const 0) + (i64.const -1) + ) + )) ) (func $less-on-equal-than-neg-one (param $x i32) (param $y i64) ;; (unsigned)x <= -1 ==> 1 From e944be281c22ce4d02a6bcae5909c4c4b3b4feca Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 28 May 2020 22:42:03 +0300 Subject: [PATCH 17/25] improve comments --- src/passes/OptimizeInstructions.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 45ba6041201..b5e2d023e20 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1418,7 +1418,7 @@ struct OptimizeInstructions binary->op = Abstract::getBinary(type, Abstract::Eq); return binary; } else if (binary->op == Abstract::getBinary(type, Abstract::Mul)) { - // (signed)x * -1 ==> -x + // (signed)x * -1 ==> 0 - x binary->op = Abstract::getBinary(type, Abstract::Sub); right->value = Literal::makeSingleZero(type); std::swap(binary->left, binary->right); @@ -1427,6 +1427,8 @@ struct OptimizeInstructions !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { // (unsigned)x <= -1 ==> 1 + // friendlier to JS emitting as we don't need to write an unsigned + // -1 value which is large. right->value = Literal::makeFromInt32(1, Type::i32); right->finalize(); return right; From f7edf1f6ba383c01a06303636d5aac0463684d5e Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 28 May 2020 22:43:03 +0300 Subject: [PATCH 18/25] fix 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 b5e2d023e20..2bab35f2961 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1411,6 +1411,8 @@ struct OptimizeInstructions return right; } else if (binary->op == Abstract::getBinary(type, Abstract::LtU)) { // (unsigned)x < -1 ==> x != -1 + // friendlier to JS emitting as we don't need to write an unsigned + // -1 value which is large. binary->op = Abstract::getBinary(type, Abstract::Ne); return binary; } else if (binary->op == Abstract::getBinary(type, Abstract::DivU)) { @@ -1427,8 +1429,6 @@ struct OptimizeInstructions !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { // (unsigned)x <= -1 ==> 1 - // friendlier to JS emitting as we don't need to write an unsigned - // -1 value which is large. right->value = Literal::makeFromInt32(1, Type::i32); right->finalize(); return right; From 1b9f1b8bb390b49391bd9276d8a458f333121f2f Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Thu, 28 May 2020 23:13:23 +0300 Subject: [PATCH 19/25] simplify --- src/passes/OptimizeInstructions.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 2bab35f2961..dd327ef7e5d 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1407,7 +1407,7 @@ struct OptimizeInstructions .hasSideEffects()) { // (unsigned)x > -1 ==> 0 right->value = Literal::makeSingleZero(Type::i32); - right->finalize(); + right->type = Type::i32; return right; } else if (binary->op == Abstract::getBinary(type, Abstract::LtU)) { // (unsigned)x < -1 ==> x != -1 @@ -1418,9 +1418,10 @@ struct OptimizeInstructions } else if (binary->op == Abstract::getBinary(type, Abstract::DivU)) { // (unsigned)x / -1 ==> x == -1 binary->op = Abstract::getBinary(type, Abstract::Eq); + binary->type = Type::i32; return binary; } else if (binary->op == Abstract::getBinary(type, Abstract::Mul)) { - // (signed)x * -1 ==> 0 - x + // x * -1 ==> 0 - x binary->op = Abstract::getBinary(type, Abstract::Sub); right->value = Literal::makeSingleZero(type); std::swap(binary->left, binary->right); @@ -1430,7 +1431,7 @@ struct OptimizeInstructions .hasSideEffects()) { // (unsigned)x <= -1 ==> 1 right->value = Literal::makeFromInt32(1, Type::i32); - right->finalize(); + right->type = Type::i32; return right; } } From 3104a2ca9ccc807c18842eece2a98c5c36c0e26f Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Fri, 29 May 2020 00:11:06 +0300 Subject: [PATCH 20/25] fix bug found by fuzzer. Unify tests cases --- src/passes/OptimizeInstructions.cpp | 3 +-- .../optimize-instructions_all-features.txt | 18 +++++++++++++----- .../optimize-instructions_all-features.wast | 16 ++++++++++------ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index dd327ef7e5d..62a975218bc 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1415,10 +1415,9 @@ struct OptimizeInstructions // -1 value which is large. binary->op = Abstract::getBinary(type, Abstract::Ne); return binary; - } else if (binary->op == Abstract::getBinary(type, Abstract::DivU)) { + } else if (binary->op == DivUInt32) { // (unsigned)x / -1 ==> x == -1 binary->op = Abstract::getBinary(type, Abstract::Eq); - binary->type = Type::i32; return binary; } else if (binary->op == Abstract::getBinary(type, Abstract::Mul)) { // x * -1 ==> 0 - x diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index 903243eb290..f325bc28fae 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -3367,7 +3367,7 @@ (i32.const 2) ) ) - (func $sub-neg-one (param $x i32) (param $y i64) + (func $rhs-is-neg-one (param $x i32) (param $y i64) (drop (i32.add (local.get $x) @@ -3380,8 +3380,6 @@ (i64.const 1) ) ) - ) - (func $greater-than-neg-one (param $x i32) (param $y i64) (drop (i32.const 0) ) @@ -3405,8 +3403,6 @@ (i32.const 0) ) ) - ) - (func $less-on-equal-than-neg-one (param $x i32) (param $y i64) (drop (i32.const 1) ) @@ -3425,6 +3421,18 @@ (i64.const -1) ) ) + (drop + (i32.eq + (local.get $x) + (i32.const -1) + ) + ) + (drop + (i64.div_u + (local.get $y) + (i64.const -1) + ) + ) ) (func $pre-combine-or (param $x i32) (param $y i32) (drop diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index c2417b97ee5..0518ab8f793 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -3821,7 +3821,7 @@ (i32.const 2) ) ) - (func $sub-neg-one (param $x i32) (param $y i64) + (func $rhs-is-neg-one (param $x i32) (param $y i64) (drop (i32.sub (local.get $x) (i32.const -1) @@ -3830,8 +3830,6 @@ (local.get $y) (i64.const -1) )) - ) - (func $greater-than-neg-one (param $x i32) (param $y i64) (drop (i32.gt_u (local.get $x) (i32.const -1) @@ -3854,8 +3852,6 @@ (i64.const -1) ) )) - ) - (func $less-on-equal-than-neg-one (param $x i32) (param $y i64) ;; (unsigned)x <= -1 ==> 1 (drop (i32.le_u (local.get $x) @@ -3865,7 +3861,6 @@ (local.get $y) (i64.const -1) )) - ;; (signed)x <= -1 ==> (unsigned)x >> sizeof(bits) - 1 (drop (i32.le_s (local.get $x) (i32.const -1) @@ -3874,6 +3869,15 @@ (local.get $y) (i64.const -1) )) + ;; (unsigned)x / -1 + (drop (i32.div_u + (local.get $x) + (i32.const -1) + )) + (drop (i64.div_u + (local.get $y) + (i64.const -1) + )) ) (func $pre-combine-or (param $x i32) (param $y i32) (drop (i32.or From cd4dfe62129c9a028f8637d022bbe4825333135e Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 2 Jun 2020 03:01:30 +0300 Subject: [PATCH 21/25] add tests for x * -1 --- test/passes/optimize-instructions_all-features.wast | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index 0518ab8f793..17bf3ac8eeb 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -3869,6 +3869,15 @@ (local.get $y) (i64.const -1) )) + ;; x * -1 + (drop (i32.mul + (local.get $x) + (i32.const -1) + )) + (drop (i64.mul + (local.get $y) + (i64.const -1) + )) ;; (unsigned)x / -1 (drop (i32.div_u (local.get $x) From fa6c38229de7b171709f9ad544c61558eb788fe4 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 2 Jun 2020 03:08:34 +0300 Subject: [PATCH 22/25] update fixtures --- test/passes/optimize-instructions_all-features.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index f325bc28fae..af994753696 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -3421,6 +3421,18 @@ (i64.const -1) ) ) + (drop + (i32.sub + (i32.const 0) + (local.get $x) + ) + ) + (drop + (i64.sub + (i64.const 0) + (local.get $y) + ) + ) (drop (i32.eq (local.get $x) From 176e5c17a903449dcb596618f31ce73962511d4e Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 2 Jun 2020 03:24:49 +0300 Subject: [PATCH 23/25] remove x - (-1) transform --- src/passes/OptimizeInstructions.cpp | 8 +------- test/passes/optimize-instructions_all-features.txt | 8 ++++---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 62a975218bc..edd330e8d79 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1381,8 +1381,7 @@ struct OptimizeInstructions } } // operations on all 1s - if (right->value == Literal(int32_t(-1)) || - right->value == Literal(int64_t(-1))) { + if (right->value.getInteger() == -1LL) { if (binary->op == Abstract::getBinary(type, Abstract::And)) { // x & -1 ==> x return binary->left; @@ -1391,11 +1390,6 @@ struct OptimizeInstructions .hasSideEffects()) { // x | -1 ==> -1 return binary->right; - } else if (binary->op == Abstract::getBinary(type, Abstract::Sub)) { - // x - (-1) ==> x + 1 - binary->op = Abstract::getBinary(type, Abstract::Add); - right->value = Literal::makeFromInt32(1, type); - return binary; } else if (binary->op == Abstract::getBinary(type, Abstract::RemS) && !EffectAnalyzer(getPassOptions(), features, binary->left) .hasSideEffects()) { diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index af994753696..5151965dff1 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -3369,15 +3369,15 @@ ) (func $rhs-is-neg-one (param $x i32) (param $y i64) (drop - (i32.add + (i32.sub (local.get $x) - (i32.const 1) + (i32.const -1) ) ) (drop - (i64.add + (i64.sub (local.get $y) - (i64.const 1) + (i64.const -1) ) ) (drop From 0c5456df8f158f626aacc1ec64b024ae30a6319b Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 2 Jun 2020 03:30:20 +0300 Subject: [PATCH 24/25] add tests x * -1 for floats --- .../optimize-instructions_all-features.txt | 18 +++++++++++++++--- .../optimize-instructions_all-features.wast | 10 +++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index 5151965dff1..225c47481b4 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -3,14 +3,14 @@ (type $i32_=>_i32 (func (param i32) (result i32))) (type $none_=>_i32 (func (result i32))) (type $none_=>_none (func)) - (type $i32_i64_=>_none (func (param i32 i64))) (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) (type $i32_=>_none (func (param i32))) + (type $i32_i64_=>_none (func (param i32 i64))) (type $none_=>_i64 (func (result i64))) (type $i64_=>_i64 (func (param i64) (result i64))) (type $i32_i64_f32_=>_none (func (param i32 i64 f32))) - (type $i32_i32_f64_f64_=>_none (func (param i32 i32 f64 f64))) (type $i32_i64_f32_f64_=>_none (func (param i32 i64 f32 f64))) + (type $i32_i32_f64_f64_=>_none (func (param i32 i32 f64 f64))) (type $i32_i64_f64_i32_=>_none (func (param i32 i64 f64 i32))) (type $none_=>_f64 (func (result f64))) (type $none_=>_anyref (func (result anyref))) @@ -3367,7 +3367,7 @@ (i32.const 2) ) ) - (func $rhs-is-neg-one (param $x i32) (param $y i64) + (func $rhs-is-neg-one (param $x i32) (param $y i64) (param $fx f32) (param $fy f64) (drop (i32.sub (local.get $x) @@ -3433,6 +3433,18 @@ (local.get $y) ) ) + (drop + (f32.mul + (local.get $fx) + (f32.const -1) + ) + ) + (drop + (f64.mul + (local.get $fy) + (f64.const -1) + ) + ) (drop (i32.eq (local.get $x) diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index 17bf3ac8eeb..a96891f9eec 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -3821,7 +3821,7 @@ (i32.const 2) ) ) - (func $rhs-is-neg-one (param $x i32) (param $y i64) + (func $rhs-is-neg-one (param $x i32) (param $y i64) (param $fx f32) (param $fy f64) (drop (i32.sub (local.get $x) (i32.const -1) @@ -3878,6 +3878,14 @@ (local.get $y) (i64.const -1) )) + (drop (f32.mul ;; skip + (local.get $fx) + (f32.const -1) + )) + (drop (f64.mul ;; skip + (local.get $fy) + (f64.const -1) + )) ;; (unsigned)x / -1 (drop (i32.div_u (local.get $x) From 7938b0136be0247633e4d0294d8199e0a398f08b Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 17 Jun 2020 00:51:20 +0300 Subject: [PATCH 25/25] add (signed)x % 1 -> 0 case --- src/passes/OptimizeInstructions.cpp | 17 +++++++++++++--- .../optimize-instructions_all-features.txt | 15 +++++++++++++- .../optimize-instructions_all-features.wast | 20 +++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index edd330e8d79..cbd8c932359 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -1363,8 +1363,9 @@ struct OptimizeInstructions auto type = binary->right->type; auto* right = binary->right->cast(); if (type.isInteger()) { + auto constRight = right->value.getInteger(); // operations on zero - if (right->value == Literal::makeFromInt32(0, type)) { + if (constRight == 0LL) { if (binary->op == Abstract::getBinary(type, Abstract::Shl) || binary->op == Abstract::getBinary(type, Abstract::ShrU) || binary->op == Abstract::getBinary(type, Abstract::ShrS) || @@ -1380,8 +1381,18 @@ struct OptimizeInstructions return Builder(*getModule()).makeUnary(EqZInt64, binary->left); } } + // operations on one + if (constRight == 1LL) { + // (signed)x % 1 ==> 0 + if (binary->op == Abstract::getBinary(type, Abstract::RemS) && + !EffectAnalyzer(getPassOptions(), features, binary->left) + .hasSideEffects()) { + right->value = Literal::makeSingleZero(type); + return right; + } + } // operations on all 1s - if (right->value.getInteger() == -1LL) { + if (constRight == -1LL) { if (binary->op == Abstract::getBinary(type, Abstract::And)) { // x & -1 ==> x return binary->left; @@ -1436,7 +1447,7 @@ struct OptimizeInstructions // subtractions than the more common additions). if (binary->op == Abstract::getBinary(type, Abstract::Add) || binary->op == Abstract::getBinary(type, Abstract::Sub)) { - auto value = right->value.getInteger(); + auto value = constRight; if (value == 0x40 || value == 0x2000 || value == 0x100000 || value == 0x8000000 || value == 0x400000000LL || value == 0x20000000000LL || value == 0x1000000000000LL || diff --git a/test/passes/optimize-instructions_all-features.txt b/test/passes/optimize-instructions_all-features.txt index 4bb730fb7a1..8da31f1077a 100644 --- a/test/passes/optimize-instructions_all-features.txt +++ b/test/passes/optimize-instructions_all-features.txt @@ -3,9 +3,9 @@ (type $i32_=>_i32 (func (param i32) (result i32))) (type $none_=>_i32 (func (result i32))) (type $none_=>_none (func)) + (type $i32_i64_=>_none (func (param i32 i64))) (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) (type $i32_=>_none (func (param i32))) - (type $i32_i64_=>_none (func (param i32 i64))) (type $none_=>_i64 (func (result i64))) (type $i64_=>_i64 (func (param i64) (result i64))) (type $i32_i64_f32_=>_none (func (param i32 i64 f32))) @@ -2735,8 +2735,21 @@ ) ) ) + (drop + (call $urem-32-power-2 + (i32.const 0) + ) + ) (unreachable) ) + (func $srem-by-1 (param $x i32) (param $y i64) + (drop + (i32.const 0) + ) + (drop + (i64.const 0) + ) + ) (func $orZero (param $0 i32) (result i32) (local.get $0) ) diff --git a/test/passes/optimize-instructions_all-features.wast b/test/passes/optimize-instructions_all-features.wast index 7e4f415f3b6..a0fff2936f0 100644 --- a/test/passes/optimize-instructions_all-features.wast +++ b/test/passes/optimize-instructions_all-features.wast @@ -3082,8 +3082,28 @@ ) ) ) + ;; (unsigned)x % 1 + (drop + (call $urem-32-power-2 + (i32.rem_u + (local.get $x) + (i32.const 1) + ) + ) + ) (unreachable) ) + (func $srem-by-1 (param $x i32) (param $y i64) + ;; (signed)x % 1 + (drop (i32.rem_s + (local.get $x) + (i32.const 1) + )) + (drop (i64.rem_s + (local.get $y) + (i64.const 1) + )) + ) (func $orZero (param $0 i32) (result i32) (i32.or (local.get $0)