Skip to content

Commit 93f69e5

Browse files
authored
[OptimizeInstructions] Combine some relational ops joined Or/And (Part 5-6) (#4372)
(i32(x) >= 0) | (i32(y) >= 0) ==> i32(x & y) >= 0 (i64(x) >= 0) | (i64(y) >= 0) ==> i64(x & y) >= 0 (i32(x) != -1) | (i32(y) != -1) ==> i32(x & y) != -1 (i64(x) != -1) | (i64(y) != -1) ==> i64(x & y) != -1
1 parent 5436efc commit 93f69e5

File tree

2 files changed

+161
-6
lines changed

2 files changed

+161
-6
lines changed

src/passes/OptimizeInstructions.cpp

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2573,6 +2573,31 @@ struct OptimizeInstructions
25732573
}
25742574
}
25752575
}
2576+
{
2577+
// Binary operations that inverses a bitwise OR to AND.
2578+
// If F(x) = binary(x, c), and F(x) inverses OR,
2579+
// that is,
2580+
//
2581+
// F(x) | F(y) == F(x & y)
2582+
//
2583+
// Then also
2584+
//
2585+
// binary(x, c) | binary(y, c) => binary(x & y, c)
2586+
Binary *bx, *by;
2587+
Expression *x, *y;
2588+
Const *cx, *cy;
2589+
if (matches(curr->left, binary(&bx, any(&x), ival(&cx))) &&
2590+
matches(curr->right, binary(&by, any(&y), ival(&cy))) &&
2591+
bx->op == by->op && x->type == y->type && cx->value == cy->value &&
2592+
inversesOr(bx)) {
2593+
by->op = Abstract::getBinary(x->type, And);
2594+
by->type = x->type;
2595+
by->left = x;
2596+
by->right = y;
2597+
bx->left = by;
2598+
return bx;
2599+
}
2600+
}
25762601
{
25772602
// Binary operations that preserve a bitwise OR can be
25782603
// reordered. If F(x) = binary(x, c), and F(x) preserves OR,
@@ -2586,14 +2611,15 @@ struct OptimizeInstructions
25862611
Binary *bx, *by;
25872612
Expression *x, *y;
25882613
Const *cx, *cy;
2589-
if (matches(curr,
2590-
binary(OrInt32,
2591-
binary(&bx, any(&x), ival(&cx)),
2592-
binary(&by, any(&y), ival(&cy)))) &&
2614+
if (matches(curr->left, binary(&bx, any(&x), ival(&cx))) &&
2615+
matches(curr->right, binary(&by, any(&y), ival(&cy))) &&
25932616
bx->op == by->op && x->type == y->type && cx->value == cy->value &&
25942617
preserveOr(bx)) {
2595-
bx->left = Builder(*getModule())
2596-
.makeBinary(Abstract::getBinary(x->type, Or), x, y);
2618+
by->op = Abstract::getBinary(x->type, Or);
2619+
by->type = x->type;
2620+
by->left = x;
2621+
by->right = y;
2622+
bx->left = by;
25972623
return bx;
25982624
}
25992625
}
@@ -2629,6 +2655,33 @@ struct OptimizeInstructions
26292655
return false;
26302656
}
26312657

2658+
// Check whether an operation inverses the Or operation to And, that is,
2659+
//
2660+
// F(x | y) = F(x) & F(y)
2661+
//
2662+
// Mathematically that means F is homomorphic with respect to the | operation.
2663+
//
2664+
// F(x) is seen as taking a single parameter of its first child. That is, the
2665+
// first child is |x|, and the rest is constant. For example, if we are given
2666+
// a binary with operation != and the right child is a constant 0, then
2667+
// F(x) = (x != 0).
2668+
bool inversesOr(Binary* curr) {
2669+
using namespace Abstract;
2670+
using namespace Match;
2671+
2672+
// (x >= 0) | (y >= 0) ==> (x & y) >= 0
2673+
if (matches(curr, binary(GeS, any(), ival(0)))) {
2674+
return true;
2675+
}
2676+
2677+
// (x !=-1) | (y !=-1) ==> (x & y) !=-1
2678+
if (matches(curr, binary(Ne, any(), ival(-1)))) {
2679+
return true;
2680+
}
2681+
2682+
return false;
2683+
}
2684+
26322685
// Check whether an operation preserves the And operation through it, that is,
26332686
//
26342687
// F(x & y) = F(x) & F(y)

test/lit/passes/optimize-instructions.wast

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11034,6 +11034,108 @@
1103411034
(i64.ne (local.get $b) (i64.const 0))
1103511035
))
1103611036
)
11037+
;; CHECK: (func $optimize-combined-by-or-gt-or-eq-to-zero (param $x i32) (param $y i32) (param $a i64) (param $b i64)
11038+
;; CHECK-NEXT: (drop
11039+
;; CHECK-NEXT: (i32.ge_s
11040+
;; CHECK-NEXT: (i32.and
11041+
;; CHECK-NEXT: (local.get $x)
11042+
;; CHECK-NEXT: (local.get $y)
11043+
;; CHECK-NEXT: )
11044+
;; CHECK-NEXT: (i32.const 0)
11045+
;; CHECK-NEXT: )
11046+
;; CHECK-NEXT: )
11047+
;; CHECK-NEXT: (drop
11048+
;; CHECK-NEXT: (i64.ge_s
11049+
;; CHECK-NEXT: (i64.and
11050+
;; CHECK-NEXT: (local.get $a)
11051+
;; CHECK-NEXT: (local.get $b)
11052+
;; CHECK-NEXT: )
11053+
;; CHECK-NEXT: (i64.const 0)
11054+
;; CHECK-NEXT: )
11055+
;; CHECK-NEXT: )
11056+
;; CHECK-NEXT: (drop
11057+
;; CHECK-NEXT: (i32.or
11058+
;; CHECK-NEXT: (i32.ge_s
11059+
;; CHECK-NEXT: (local.get $x)
11060+
;; CHECK-NEXT: (i32.const 0)
11061+
;; CHECK-NEXT: )
11062+
;; CHECK-NEXT: (i64.ge_s
11063+
;; CHECK-NEXT: (local.get $a)
11064+
;; CHECK-NEXT: (i64.const 0)
11065+
;; CHECK-NEXT: )
11066+
;; CHECK-NEXT: )
11067+
;; CHECK-NEXT: )
11068+
;; CHECK-NEXT: )
11069+
(func $optimize-combined-by-or-gt-or-eq-to-zero (param $x i32) (param $y i32) (param $a i64) (param $b i64)
11070+
;; (i32(x) >= 0) | (i32(y) >= 0) ==> i32(x & y) >= 0
11071+
(drop (i32.or
11072+
(i32.ge_s (local.get $x) (i32.const 0))
11073+
(i32.ge_s (local.get $y) (i32.const 0))
11074+
))
11075+
;; (i64(x) >= 0) | (i64(y) >= 0) ==> i64(x & y) >= 0
11076+
(drop (i32.or
11077+
(i64.ge_s (local.get $a) (i64.const 0))
11078+
(i64.ge_s (local.get $b) (i64.const 0))
11079+
))
11080+
11081+
;; skips
11082+
;; (i32(x) >= 0) | (i64(y) >= 0) ==> skip
11083+
(drop (i32.or
11084+
(i32.ge_s (local.get $x) (i32.const 0))
11085+
(i64.ge_s (local.get $a) (i64.const 0))
11086+
))
11087+
)
11088+
;; CHECK: (func $optimize-combined-by-or-ne-to-minus-one (param $x i32) (param $y i32) (param $a i64) (param $b i64)
11089+
;; CHECK-NEXT: (drop
11090+
;; CHECK-NEXT: (i32.ne
11091+
;; CHECK-NEXT: (i32.and
11092+
;; CHECK-NEXT: (local.get $x)
11093+
;; CHECK-NEXT: (local.get $y)
11094+
;; CHECK-NEXT: )
11095+
;; CHECK-NEXT: (i32.const -1)
11096+
;; CHECK-NEXT: )
11097+
;; CHECK-NEXT: )
11098+
;; CHECK-NEXT: (drop
11099+
;; CHECK-NEXT: (i64.ne
11100+
;; CHECK-NEXT: (i64.and
11101+
;; CHECK-NEXT: (local.get $a)
11102+
;; CHECK-NEXT: (local.get $b)
11103+
;; CHECK-NEXT: )
11104+
;; CHECK-NEXT: (i64.const -1)
11105+
;; CHECK-NEXT: )
11106+
;; CHECK-NEXT: )
11107+
;; CHECK-NEXT: (drop
11108+
;; CHECK-NEXT: (i32.or
11109+
;; CHECK-NEXT: (i32.ne
11110+
;; CHECK-NEXT: (local.get $x)
11111+
;; CHECK-NEXT: (i32.const -1)
11112+
;; CHECK-NEXT: )
11113+
;; CHECK-NEXT: (i64.ne
11114+
;; CHECK-NEXT: (local.get $a)
11115+
;; CHECK-NEXT: (i64.const -1)
11116+
;; CHECK-NEXT: )
11117+
;; CHECK-NEXT: )
11118+
;; CHECK-NEXT: )
11119+
;; CHECK-NEXT: )
11120+
(func $optimize-combined-by-or-ne-to-minus-one (param $x i32) (param $y i32) (param $a i64) (param $b i64)
11121+
;; (i32(x) != -1) | (i32(y) != -1) ==> i32(x & y) != -1
11122+
(drop (i32.or
11123+
(i32.ne (local.get $x) (i32.const -1))
11124+
(i32.ne (local.get $y) (i32.const -1))
11125+
))
11126+
;; (i64(x) != -1) | (i64(y) == -1) ==> i64(x & y) != -1
11127+
(drop (i32.or
11128+
(i64.ne (local.get $a) (i64.const -1))
11129+
(i64.ne (local.get $b) (i64.const -1))
11130+
))
11131+
11132+
;; skips
11133+
;; (i32(x) != -1) | (i64(y) != -1) ==> skip
11134+
(drop (i32.or
11135+
(i32.ne (local.get $x) (i32.const -1))
11136+
(i64.ne (local.get $a) (i64.const -1))
11137+
))
11138+
)
1103711139
;; CHECK: (func $optimize-combined-by-or-lessthan-zero (param $x i32) (param $y i32) (param $a i64) (param $b i64)
1103811140
;; CHECK-NEXT: (drop
1103911141
;; CHECK-NEXT: (i32.lt_s

0 commit comments

Comments
 (0)