diff options
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 61 | ||||
-rw-r--r-- | test/lit/passes/optimize-instructions.wast | 117 | ||||
-rw-r--r-- | test/passes/asyncify_mod-asyncify-never-unwind_O.txt | 8 | ||||
-rw-r--r-- | test/passes/asyncify_optimize-level=1.txt | 112 |
4 files changed, 238 insertions, 60 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 609caf588..b6ebb961a 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -864,6 +864,7 @@ struct OptimizeInstructions if (auto* ret = optimizeSelect(curr)) { return replaceCurrent(ret); } + optimizeTernary(curr, curr->condition, curr->ifTrue, curr->ifFalse); } void visitGlobalSet(GlobalSet* curr) { @@ -914,6 +915,7 @@ struct OptimizeInstructions return replaceCurrent(ret); } } + optimizeTernary(curr, curr->condition, curr->ifTrue, curr->ifFalse); } } @@ -2779,6 +2781,65 @@ private: return false; } } + + // Optimize an if-else or a select, something with a condition and two + // arms with outputs. + void optimizeTernary(Expression* curr, + Expression* condition, + Expression*& ifTrue, + Expression*& ifFalse) { + if (curr->type == Type::unreachable) { + return; + } + + using namespace Match; + Builder builder(*getModule()); + + // If one arm is an operation and the other is an appropriate constant, we + // can move the operation outside (where it may be further optimized), e.g. + // + // (select + // (i32.eqz (X)) + // (i32.const 0|1) + // (Y) + // ) + // => + // (i32.eqz + // (select + // (X) + // (i32.const 1|0) + // (Y) + // ) + // ) + { + Unary* un; + Expression* x; + Const* c; + auto check = [&](Expression* a, Expression* b) { + if (matches(a, unary(&un, EqZInt32, any(&x))) && matches(b, ival(&c))) { + auto value = c->value.geti32(); + return value == 0 || value == 1; + } + return false; + }; + if (check(ifTrue, ifFalse) || check(ifFalse, ifTrue)) { + auto updateArm = [&](Expression* arm) -> Expression* { + if (arm == un) { + // This is the arm that had the eqz, which we need to remove. + return un->value; + } else { + // This is the arm with the constant, which we need to flip. + c->value = Literal(int32_t(1 - c->value.geti32())); + return c; + } + }; + ifTrue = updateArm(ifTrue); + ifFalse = updateArm(ifFalse); + un->value = curr; + return replaceCurrent(un); + } + } + } }; Pass* createOptimizeInstructionsPass() { return new OptimizeInstructions; } diff --git a/test/lit/passes/optimize-instructions.wast b/test/lit/passes/optimize-instructions.wast index 5a180e3a5..bb05f17ba 100644 --- a/test/lit/passes/optimize-instructions.wast +++ b/test/lit/passes/optimize-instructions.wast @@ -11701,4 +11701,121 @@ (f64.abs (f64.add (local.get $x0) (local.get $x0))) )) ) + ;; CHECK: (func $ternary (param $x i32) (param $y i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ternary (param $x i32) (param $y i32) + (drop + (select + (i32.const 0) + (i32.eqz + (local.get $y) + ) + (local.get $x) + ) + ) + (drop + (select + (i32.const 1) + (i32.eqz + (local.get $y) + ) + (local.get $x) + ) + ) + (drop + (select + (i32.eqz + (local.get $y) + ) + (i32.const 0) + (local.get $x) + ) + ) + (drop + (select + (i32.eqz + (local.get $y) + ) + (i32.const 1) + (local.get $x) + ) + ) + ;; if works too + (drop + (if (result i32) + (local.get $x) + (i32.eqz + (local.get $y) + ) + (i32.const 1) + ) + ) + ) + ;; CHECK: (func $ternary-no (param $x i32) (param $y i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ternary-no (param $x i32) (param $y i32) + (drop + (select + (i32.const 2) ;; only 0 and 1 work + (i32.eqz + (local.get $y) + ) + (local.get $x) + ) + ) + ) ) diff --git a/test/passes/asyncify_mod-asyncify-never-unwind_O.txt b/test/passes/asyncify_mod-asyncify-never-unwind_O.txt index d9308c21c..18f55e79e 100644 --- a/test/passes/asyncify_mod-asyncify-never-unwind_O.txt +++ b/test/passes/asyncify_mod-asyncify-never-unwind_O.txt @@ -17,8 +17,8 @@ (export "asyncify_get_state" (func $asyncify_get_state)) (func $calls-import (; has Stack IR ;) (if - (select - (i32.eqz + (i32.eqz + (select (if (result i32) (i32.eq (global.get $__asyncify_state) @@ -42,9 +42,9 @@ ) (i32.const 0) ) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (call $import) ) diff --git a/test/passes/asyncify_optimize-level=1.txt b/test/passes/asyncify_optimize-level=1.txt index 2c8835edf..9909918f2 100644 --- a/test/passes/asyncify_optimize-level=1.txt +++ b/test/passes/asyncify_optimize-level=1.txt @@ -19,8 +19,8 @@ (local.set $0 (block $__asyncify_unwind (result i32) (if - (select - (i32.eqz + (i32.eqz + (select (if (result i32) (i32.eq (global.get $__asyncify_state) @@ -44,9 +44,9 @@ ) (local.get $0) ) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (call $import) @@ -110,8 +110,8 @@ (local.set $1 (block $__asyncify_unwind (result i32) (if - (select - (i32.eqz + (i32.eqz + (select (if (result i32) (i32.eq (global.get $__asyncify_state) @@ -135,9 +135,9 @@ ) (local.get $1) ) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (local.set $1 @@ -205,8 +205,8 @@ (local.set $0 (block $__asyncify_unwind (result i32) (if - (select - (i32.eqz + (i32.eqz + (select (if (result i32) (i32.eq (global.get $__asyncify_state) @@ -230,9 +230,9 @@ ) (local.get $0) ) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (drop @@ -343,12 +343,12 @@ ) ) (if - (select - (i32.eqz + (i32.eqz + (select (local.get $2) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (call $import) @@ -443,12 +443,12 @@ ) ) (if - (select - (i32.eqz + (i32.eqz + (select (local.get $1) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (call $import) @@ -551,12 +551,12 @@ ) ) (if - (select - (i32.eqz + (i32.eqz + (select (local.get $2) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (call $import3 @@ -706,12 +706,12 @@ ) ) (if - (select - (i32.eqz + (i32.eqz + (select (local.get $1) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (call $import3 @@ -825,12 +825,12 @@ ) ) (if - (select - (i32.eqz + (i32.eqz + (select (local.get $2) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (call $import3 @@ -964,12 +964,12 @@ ) (loop $l (if - (select - (i32.eqz + (i32.eqz + (select (local.get $1) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (call $import3 @@ -1090,12 +1090,12 @@ ) (loop $l (if - (select - (i32.eqz + (i32.eqz + (select (local.get $2) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (local.set $1 @@ -1116,12 +1116,12 @@ ) ) (br_if $l - (select - (i32.eqz + (i32.eqz + (select (global.get $__asyncify_state) + (i32.const 1) + (local.get $0) ) - (i32.const 0) - (local.get $0) ) ) ) @@ -1194,12 +1194,12 @@ (call $boring) ) (if - (select - (i32.eqz + (i32.eqz + (select (local.get $0) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (call $import) @@ -1299,12 +1299,12 @@ (call $boring-deep) ) (if - (select - (i32.eqz + (i32.eqz + (select (local.get $0) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (call $import-deep) @@ -1374,8 +1374,8 @@ (local.set $0 (block $__asyncify_unwind (result i32) (if - (select - (i32.eqz + (i32.eqz + (select (if (result i32) (i32.eq (global.get $__asyncify_state) @@ -1399,9 +1399,9 @@ ) (local.get $0) ) + (i32.const 0) + (global.get $__asyncify_state) ) - (i32.const 1) - (global.get $__asyncify_state) ) (block (call $import) |