Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 63 additions & 12 deletions src/passes/OptimizeInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2510,11 +2510,36 @@ struct OptimizeInstructions
if (matches(curr->left, unary(EqZ, any(&x))) &&
matches(curr->right, unary(EqZ, any(&y))) && x->type == y->type) {
auto* inner = curr->left->cast<Unary>();
inner->value = Builder(*getModule())
.makeBinary(Abstract::getBinary(x->type, Or), x, y);
inner->value =
Builder(*getModule()).makeBinary(getBinary(x->type, Or), x, y);
return inner;
}
}
{
// Binary operations that inverse a bitwise AND can be
// reordered. If F(x) = binary(x, c), and F(x) preserves AND,
// that is,
//
// F(x) & F(y) == F(x | y)
//
// Then also
//
// binary(x, c) & binary(y, c) => binary(x | y, c)
Binary *bx, *by;
Expression *x, *y;
Const *cx, *cy;
if (matches(curr->left, binary(&bx, any(&x), ival(&cx))) &&
matches(curr->right, binary(&by, any(&y), ival(&cy))) &&
bx->op == by->op && x->type == y->type && cx->value == cy->value &&
inversesAnd(bx)) {
by->op = getBinary(x->type, Or);
by->type = x->type;
by->left = x;
by->right = y;
bx->left = by;
return bx;
}
}
{
// Binary operations that preserve a bitwise AND can be
// reordered. If F(x) = binary(x, c), and F(x) preserves AND,
Expand All @@ -2528,14 +2553,15 @@ struct OptimizeInstructions
Binary *bx, *by;
Expression *x, *y;
Const *cx, *cy;
if (matches(curr,
binary(AndInt32,
binary(&bx, any(&x), ival(&cx)),
binary(&by, any(&y), ival(&cy)))) &&
if (matches(curr->left, binary(&bx, any(&x), ival(&cx))) &&
matches(curr->right, binary(&by, any(&y), ival(&cy))) &&
bx->op == by->op && x->type == y->type && cx->value == cy->value &&
preserveAnd(bx)) {
bx->left = Builder(*getModule())
.makeBinary(Abstract::getBinary(x->type, And), x, y);
by->op = getBinary(x->type, And);
by->type = x->type;
by->left = x;
by->right = y;
bx->left = by;
return bx;
}
}
Expand Down Expand Up @@ -2590,7 +2616,7 @@ struct OptimizeInstructions
matches(curr->right, binary(&by, any(&y), ival(&cy))) &&
bx->op == by->op && x->type == y->type && cx->value == cy->value &&
inversesOr(bx)) {
by->op = Abstract::getBinary(x->type, And);
by->op = getBinary(x->type, And);
by->type = x->type;
by->left = x;
by->right = y;
Expand All @@ -2615,7 +2641,7 @@ struct OptimizeInstructions
matches(curr->right, binary(&by, any(&y), ival(&cy))) &&
bx->op == by->op && x->type == y->type && cx->value == cy->value &&
preserveOr(bx)) {
by->op = Abstract::getBinary(x->type, Or);
by->op = getBinary(x->type, Or);
by->type = x->type;
by->left = x;
by->right = y;
Expand Down Expand Up @@ -2645,13 +2671,11 @@ struct OptimizeInstructions
if (matches(curr, binary(Ne, any(), ival(0)))) {
return true;
}

// (x < 0) | (y < 0) ==> (x | y) < 0
// This effectively checks if x or y have the sign bit set.
if (matches(curr, binary(LtS, any(), ival(0)))) {
return true;
}

return false;
}

Expand Down Expand Up @@ -2701,6 +2725,33 @@ struct OptimizeInstructions
return true;
}

// (x == -1) & (y == -1) ==> (x & y) == -1
if (matches(curr, binary(Eq, any(), ival(-1)))) {
return true;
}

return false;
}

// Check whether an operation inverses the And operation to Or, that is,
//
// F(x & y) = F(x) | F(y)
//
// Mathematically that means F is homomorphic with respect to the & operation.
//
// F(x) is seen as taking a single parameter of its first child. That is, the
// first child is |x|, and the rest is constant. For example, if we are given
// a binary with operation != and the right child is a constant 0, then
// F(x) = (x != 0).
bool inversesAnd(Binary* curr) {
using namespace Abstract;
using namespace Match;

// (x >= 0) & (y >= 0) ==> (x | y) >= 0
if (matches(curr, binary(GeS, any(), ival(0)))) {
return true;
}

return false;
}

Expand Down
102 changes: 102 additions & 0 deletions test/lit/passes/optimize-instructions.wast
Original file line number Diff line number Diff line change
Expand Up @@ -11253,6 +11253,108 @@
(i64.lt_s (local.get $b) (i64.const 0))
))
)
;; CHECK: (func $optimize-combined-by-and-greatequal-zero (param $x i32) (param $y i32) (param $a i64) (param $b i64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (i32.or
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i64.ge_s
;; CHECK-NEXT: (i64.or
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: (local.get $b)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (i32.ge_s
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i64.ge_s
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: (i64.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $optimize-combined-by-and-greatequal-zero (param $x i32) (param $y i32) (param $a i64) (param $b i64)
;; (i32(x) >= 0) & (i32(y) >= 0) ==> i32(x | y) >= 0
(drop (i32.and
(i32.ge_s (local.get $x) (i32.const 0))
(i32.ge_s (local.get $y) (i32.const 0))
))
;; (i64(x) >= 0) & (i64(y) >= 0) ==> i64(x | y) >= 0
(drop (i32.and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be i64? Does binaryen not error on this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, result type of LHS and RHS comparisons are i32. Also all this optimizations only apply for i32 and / or.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, thanks, I forgot that the comparisons return a bool, which is i32...

(i64.ge_s (local.get $a) (i64.const 0))
(i64.ge_s (local.get $b) (i64.const 0))
))

;; skips
;; (i32(x) >= 0) & (i64(y) >= 0) ==> skip
(drop (i32.and
(i32.ge_s (local.get $x) (i32.const 0))
(i64.ge_s (local.get $a) (i64.const 0))
))
)
;; CHECK: (func $optimize-combined-by-and-equal-neg-one (param $x i32) (param $y i32) (param $a i64) (param $b i64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.eq
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const -1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i64.eq
;; CHECK-NEXT: (i64.and
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: (local.get $b)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i64.const -1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.and
;; CHECK-NEXT: (i32.eq
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const -1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i64.eq
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: (i64.const -1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $optimize-combined-by-and-equal-neg-one (param $x i32) (param $y i32) (param $a i64) (param $b i64)
;; (i32(x) == -1) & (i32(y) == -1) ==> i32(x & y) == -1
(drop (i32.and
(i32.eq (local.get $x) (i32.const -1))
(i32.eq (local.get $y) (i32.const -1))
))
;; (i64(x) == -1) & (i64(y) == -1) ==> i64(x & y) == -1
(drop (i32.and
(i64.eq (local.get $a) (i64.const -1))
(i64.eq (local.get $b) (i64.const -1))
))

;; skips
;; (i32(x) == -1) & (i64(y) == -1) ==> skip
(drop (i32.and
(i32.eq (local.get $x) (i32.const -1))
(i64.eq (local.get $a) (i64.const -1))
))
)
;; CHECK: (func $optimize-relationals (param $x i32) (param $y i32) (param $X i64) (param $Y i64)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.eq
Expand Down