Skip to content

Commit c9ec43d

Browse files
OptimizeInstructions: Max bits on left and constant on right have no overlap => 0 (#7505)
E.g. (i32.and (i32.eqz (i32.load $0 (i32.const 0) ) ) (i32.const 4) ) => (i32.const 0) Fixes: #7481
1 parent 3da0ceb commit c9ec43d

File tree

2 files changed

+239
-9
lines changed

2 files changed

+239
-9
lines changed

src/passes/OptimizeInstructions.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,9 @@ struct OptimizeInstructions
839839
if (auto* ret = combineAnd(curr)) {
840840
return replaceCurrent(ret);
841841
}
842+
if (auto* ret = optimizeAndNoOverlappingBits(curr)) {
843+
return replaceCurrent(ret);
844+
}
842845
}
843846
// for or, we can potentially combine
844847
if (curr->op == OrInt32) {
@@ -852,6 +855,12 @@ struct OptimizeInstructions
852855
return replaceCurrent(ret);
853856
}
854857
}
858+
if (curr->op == AndInt64) {
859+
if (auto* ret = optimizeAndNoOverlappingBits(curr)) {
860+
return replaceCurrent(ret);
861+
}
862+
}
863+
855864
// relation/comparisons allow for math optimizations
856865
if (curr->isRelational()) {
857866
if (auto* ret = optimizeRelational(curr)) {
@@ -3566,6 +3575,35 @@ struct OptimizeInstructions
35663575
return nullptr;
35673576
}
35683577

3578+
// Bitwise AND of a value with bits in [0, n) and a constant with no bits in
3579+
// [0, n) always yields 0. Replace with zero.
3580+
Expression* optimizeAndNoOverlappingBits(Binary* curr) {
3581+
assert(curr->op == AndInt32 || curr->op == AndInt64);
3582+
3583+
auto* left = curr->left;
3584+
auto* right = curr->right;
3585+
3586+
// Check left's max bits and right is constant.
3587+
auto leftMaxBits = Bits::getMaxBits(left, this);
3588+
uint64_t maskLeft;
3589+
if (!left->type.isNumber() || leftMaxBits == left->type.getByteSize() * 8) {
3590+
// If we know nothing useful about the bits on the left,
3591+
// we cannot optimize.
3592+
return nullptr;
3593+
} else {
3594+
maskLeft = (1ULL << leftMaxBits) - 1;
3595+
}
3596+
if (auto* c = right->dynCast<Const>()) {
3597+
uint64_t constantValue = c->value.getInteger();
3598+
if ((constantValue & maskLeft) == 0) {
3599+
return getDroppedChildrenAndAppend(
3600+
curr, LiteralUtils::makeZero(left->type, *getModule()));
3601+
}
3602+
}
3603+
3604+
return nullptr;
3605+
}
3606+
35693607
// We can combine `or` operations, e.g.
35703608
// (x > y) | (x == y) ==> x >= y
35713609
// (x != 0) | (y != 0) ==> (x | y) != 0

test/lit/passes/optimize-instructions-mvp.wast

Lines changed: 201 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,9 +1770,12 @@
17701770
)
17711771
;; CHECK: (func $canonicalize-consts-vars (param $x i32) (param $y i32)
17721772
;; CHECK-NEXT: (drop
1773+
;; CHECK-NEXT: (i32.const 0)
1774+
;; CHECK-NEXT: )
1775+
;; CHECK-NEXT: (drop
17731776
;; CHECK-NEXT: (i32.and
1774-
;; CHECK-NEXT: (i32.const 1)
17751777
;; CHECK-NEXT: (i32.const 2)
1778+
;; CHECK-NEXT: (i32.const 1)
17761779
;; CHECK-NEXT: )
17771780
;; CHECK-NEXT: )
17781781
;; CHECK-NEXT: (drop
@@ -1810,6 +1813,7 @@
18101813
;; CHECK-NEXT: )
18111814
(func $canonicalize-consts-vars (param $x i32) (param $y i32)
18121815
(drop (i32.and (i32.const 1) (i32.const 2)))
1816+
(drop (i32.and (i32.const 2) (i32.const 1)))
18131817
(drop (i32.and (local.get $x) (i32.const 3)))
18141818
(drop (i32.and (i32.const 4) (local.get $x)))
18151819
(drop (i32.and (local.get $x) (local.get $y)))
@@ -3193,18 +3197,18 @@
31933197
(i32.const 24)
31943198
)
31953199
)
3196-
;; CHECK: (func $sext-24-and-127-128 (result i32)
3200+
;; CHECK: (func $sext-24-and-127-unknown (param $x i32) (result i32)
31973201
;; CHECK-NEXT: (i32.and
3202+
;; CHECK-NEXT: (local.get $x)
31983203
;; CHECK-NEXT: (i32.const 127)
3199-
;; CHECK-NEXT: (i32.const 128)
32003204
;; CHECK-NEXT: )
32013205
;; CHECK-NEXT: )
3202-
(func $sext-24-and-127-128 (result i32)
3206+
(func $sext-24-and-127-unknown (param $x i32) (result i32)
32033207
(i32.shr_s
32043208
(i32.shl
32053209
(i32.and ;; takes the min, here it is ok
32063210
(i32.const 127)
3207-
(i32.const 128)
3211+
(local.get $x)
32083212
)
32093213
(i32.const 24)
32103214
)
@@ -7300,7 +7304,7 @@
73007304
)
73017305
)
73027306
)
7303-
;; CHECK: (func $de-morgan-2 (param $x i32) (param $y i32)
7307+
;; CHECK: (func $de-morgan-2 (param $x i32) (param $y i32) (param $z i64)
73047308
;; CHECK-NEXT: (drop
73057309
;; CHECK-NEXT: (i32.eqz
73067310
;; CHECK-NEXT: (i32.or
@@ -7350,7 +7354,9 @@
73507354
;; CHECK-NEXT: (i32.eqz
73517355
;; CHECK-NEXT: (local.get $x)
73527356
;; CHECK-NEXT: )
7353-
;; CHECK-NEXT: (i32.const 2)
7357+
;; CHECK-NEXT: (i32.wrap_i64
7358+
;; CHECK-NEXT: (local.get $z)
7359+
;; CHECK-NEXT: )
73547360
;; CHECK-NEXT: )
73557361
;; CHECK-NEXT: )
73567362
;; CHECK-NEXT: (drop
@@ -7359,7 +7365,7 @@
73597365
;; CHECK-NEXT: )
73607366
;; CHECK-NEXT: )
73617367
;; CHECK-NEXT: )
7362-
(func $de-morgan-2 (param $x i32) (param $y i32)
7368+
(func $de-morgan-2 (param $x i32) (param $y i32) (param $z i64)
73637369
(drop
73647370
(i32.and (i32.eqz (local.get $x)) (i32.eqz (local.get $y)))
73657371
)
@@ -7376,7 +7382,7 @@
73767382
(i32.and (local.get $x) (i32.eqz (local.get $y)))
73777383
)
73787384
(drop
7379-
(i32.and (i32.eqz (local.get $x)) (i32.wrap_i64 (i64.const 2)))
7385+
(i32.and (i32.eqz (local.get $x)) (i32.wrap_i64 (local.get $z)))
73807386
)
73817387
(drop
73827388
(i32.and (i32.wrap_i64 (i64.const 1)) (i32.eqz (local.get $y)))
@@ -18218,4 +18224,190 @@
1821818224
(i32.const 1)
1821918225
)
1822018226
)
18227+
;; CHECK: (func $add-op-no-overlapping-bits-corner-case (param $0 i32) (param $1 i64)
18228+
;; CHECK-NEXT: (drop
18229+
;; CHECK-NEXT: (i32.const 0)
18230+
;; CHECK-NEXT: )
18231+
;; CHECK-NEXT: (drop
18232+
;; CHECK-NEXT: (i64.const 0)
18233+
;; CHECK-NEXT: )
18234+
;; CHECK-NEXT: (drop
18235+
;; CHECK-NEXT: (i64.const 0)
18236+
;; CHECK-NEXT: )
18237+
;; CHECK-NEXT: (drop
18238+
;; CHECK-NEXT: (i32.const 0)
18239+
;; CHECK-NEXT: )
18240+
;; CHECK-NEXT: (drop
18241+
;; CHECK-NEXT: (i64.const 0)
18242+
;; CHECK-NEXT: )
18243+
;; CHECK-NEXT: )
18244+
(func $add-op-no-overlapping-bits-corner-case (param $0 i32) (param $1 i64)
18245+
;; optimizeAndNoOverlappingBits simplifies AND operations where
18246+
;; - the left value covers bits in [0, n)
18247+
;; - the right operand is a constant with no bits in [0, n)
18248+
;; Result is simplified to zero.
18249+
;; No bit overlaps, so we optimize.
18250+
(drop
18251+
(i32.and
18252+
(i32.const 1)
18253+
(i32.const 2)
18254+
)
18255+
)
18256+
(drop
18257+
(i64.and
18258+
(i64.const 1)
18259+
(i64.const 2)
18260+
)
18261+
)
18262+
(drop
18263+
(i64.and
18264+
(i64.const 0x7fffffff)
18265+
(i64.const 0x80000000)
18266+
)
18267+
)
18268+
;; We know something (but not constant) about the bits
18269+
;; on the left, so we can optimize.
18270+
(drop
18271+
(i32.and
18272+
(i32.and
18273+
(local.get $0)
18274+
(i32.const 0xff)
18275+
)
18276+
(i32.const 0xff00)
18277+
)
18278+
)
18279+
(drop
18280+
(i64.and
18281+
(i64.and
18282+
(local.get $1)
18283+
(i64.const 0xff)
18284+
)
18285+
(i64.const 0xff00)
18286+
)
18287+
)
18288+
)
18289+
;; CHECK: (func $add-op-overlapping-bits-corner-case
18290+
;; CHECK-NEXT: (drop
18291+
;; CHECK-NEXT: (i32.and
18292+
;; CHECK-NEXT: (i32.const 2147483647)
18293+
;; CHECK-NEXT: (i32.const -2147483647)
18294+
;; CHECK-NEXT: )
18295+
;; CHECK-NEXT: )
18296+
;; CHECK-NEXT: (drop
18297+
;; CHECK-NEXT: (i64.and
18298+
;; CHECK-NEXT: (i64.const 2147483647)
18299+
;; CHECK-NEXT: (i64.const 2147483649)
18300+
;; CHECK-NEXT: )
18301+
;; CHECK-NEXT: )
18302+
;; CHECK-NEXT: )
18303+
(func $add-op-overlapping-bits-corner-case
18304+
;; One bit overlaps, so we cannot optimize.
18305+
(drop
18306+
(i32.and
18307+
(i32.const 0x7fffffff)
18308+
(i32.const 0x80000001)
18309+
)
18310+
)
18311+
(drop
18312+
(i64.and
18313+
(i64.const 0x7fffffff)
18314+
(i64.const 0x80000001)
18315+
)
18316+
)
18317+
)
18318+
;; CHECK: (func $add-op-no-overlapping-skipped
18319+
;; CHECK-NEXT: (drop
18320+
;; CHECK-NEXT: (i32.and
18321+
;; CHECK-NEXT: (i32.const 2)
18322+
;; CHECK-NEXT: (i32.const 1)
18323+
;; CHECK-NEXT: )
18324+
;; CHECK-NEXT: )
18325+
;; CHECK-NEXT: (drop
18326+
;; CHECK-NEXT: (i64.and
18327+
;; CHECK-NEXT: (i64.const 2)
18328+
;; CHECK-NEXT: (i64.const 1)
18329+
;; CHECK-NEXT: )
18330+
;; CHECK-NEXT: )
18331+
;; CHECK-NEXT: (drop
18332+
;; CHECK-NEXT: (i64.and
18333+
;; CHECK-NEXT: (i64.const 2147483648)
18334+
;; CHECK-NEXT: (i64.const 2147483647)
18335+
;; CHECK-NEXT: )
18336+
;; CHECK-NEXT: )
18337+
;; CHECK-NEXT: )
18338+
(func $add-op-no-overlapping-skipped
18339+
;; Both-constant cases which do not meet the condition (mask of left has no
18340+
;; overlap with right) is left for Precompute.
18341+
(drop
18342+
(i32.and
18343+
(i32.const 2)
18344+
(i32.const 1)
18345+
)
18346+
)
18347+
(drop
18348+
(i64.and
18349+
(i64.const 2)
18350+
(i64.const 1)
18351+
)
18352+
)
18353+
(drop
18354+
(i64.and
18355+
(i64.const 0x80000000)
18356+
(i64.const 0x7fffffff)
18357+
)
18358+
)
18359+
)
18360+
;; CHECK: (func $add-op-unknown-useful (param $0 i32) (param $1 i64)
18361+
;; CHECK-NEXT: (drop
18362+
;; CHECK-NEXT: (i32.and
18363+
;; CHECK-NEXT: (i32.const -2147483648)
18364+
;; CHECK-NEXT: (i32.const 2147483647)
18365+
;; CHECK-NEXT: )
18366+
;; CHECK-NEXT: )
18367+
;; CHECK-NEXT: (drop
18368+
;; CHECK-NEXT: (i64.and
18369+
;; CHECK-NEXT: (i64.const -9223372036854775808)
18370+
;; CHECK-NEXT: (i64.const 9223372036854775807)
18371+
;; CHECK-NEXT: )
18372+
;; CHECK-NEXT: )
18373+
;; CHECK-NEXT: (drop
18374+
;; CHECK-NEXT: (i32.and
18375+
;; CHECK-NEXT: (local.get $0)
18376+
;; CHECK-NEXT: (i32.const 1)
18377+
;; CHECK-NEXT: )
18378+
;; CHECK-NEXT: )
18379+
;; CHECK-NEXT: (drop
18380+
;; CHECK-NEXT: (i64.and
18381+
;; CHECK-NEXT: (local.get $1)
18382+
;; CHECK-NEXT: (i64.const 1)
18383+
;; CHECK-NEXT: )
18384+
;; CHECK-NEXT: )
18385+
;; CHECK-NEXT: )
18386+
(func $add-op-unknown-useful (param $0 i32) (param $1 i64)
18387+
;; We know nothing useful about the bits on the left, so we cannot optimize.
18388+
(drop
18389+
(i32.and
18390+
(i32.const 0x80000000)
18391+
(i32.const 0x7fffffff)
18392+
)
18393+
)
18394+
(drop
18395+
(i64.and
18396+
(i64.const 0x8000000000000000)
18397+
(i64.const 0x7fffffffffffffff)
18398+
)
18399+
)
18400+
(drop
18401+
(i32.and
18402+
(local.get $0)
18403+
(i32.const 1)
18404+
)
18405+
)
18406+
(drop
18407+
(i64.and
18408+
(local.get $1)
18409+
(i64.const 1)
18410+
)
18411+
)
18412+
)
1822118413
)

0 commit comments

Comments
 (0)