Skip to content

Commit 60cc473

Browse files
authored
Optimize unsigned divisions when rhs is negative constant (#2991)
`(uint32_t)x / C` --> `x >= C`, where `C > 2^31` `(uint32_t)x / -1` --> `x != -1` and for `shrinkLevel == 0`: `(uint64_t)x / C` --> `uint64_t(x >= C)`, where `C > 2^63` `(uint64_t)x / -1` --> `x != -1`
1 parent 689a6fc commit 60cc473

File tree

3 files changed

+98
-9
lines changed

3 files changed

+98
-9
lines changed

src/passes/OptimizeInstructions.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,6 @@ struct OptimizeInstructions
537537
}
538538
}
539539
}
540-
// math operations on a constant power of 2 right side can be optimized
541540
if (right->type == Type::i32) {
542541
BinaryOp op;
543542
int32_t c = right->value.geti32();
@@ -550,6 +549,15 @@ struct OptimizeInstructions
550549
Bits::getMaxBits(binary->left, this) <= 31) {
551550
binary->op = op;
552551
}
552+
if (c < 0 && c > std::numeric_limits<int32_t>::min() &&
553+
binary->op == DivUInt32) {
554+
// u32(x) / C ==> u32(x) >= C iff C > 2^31
555+
// We avoid applying this for C == 2^31 due to conflict
556+
// with other rule which transform to more prefereble
557+
// right shift operation.
558+
binary->op = c == -1 ? EqInt32 : GeUInt32;
559+
return binary;
560+
}
553561
if (Bits::isPowerOf2((uint32_t)c)) {
554562
switch (binary->op) {
555563
case MulInt32:
@@ -572,6 +580,19 @@ struct OptimizeInstructions
572580
Bits::getMaxBits(binary->left, this) <= 63) {
573581
binary->op = op;
574582
}
583+
if (getPassOptions().shrinkLevel == 0 && c < 0 &&
584+
c > std::numeric_limits<int64_t>::min() &&
585+
binary->op == DivUInt64) {
586+
// u64(x) / C ==> u64(u64(x) >= C) iff C > 2^63
587+
// We avoid applying this for C == 2^31 due to conflict
588+
// with other rule which transform to more prefereble
589+
// right shift operation.
590+
// And apply this only for shrinkLevel == 0 due to it
591+
// increasing size by one byte.
592+
binary->op = c == -1LL ? EqInt64 : GeUInt64;
593+
binary->type = Type::i32;
594+
return Builder(*getModule()).makeUnary(ExtendUInt32, binary);
595+
}
575596
if (Bits::isPowerOf2((uint64_t)c)) {
576597
switch (binary->op) {
577598
case MulInt64:
@@ -1398,12 +1419,6 @@ struct OptimizeInstructions
13981419
curr->op = Abstract::getBinary(type, Abstract::Ne);
13991420
return curr;
14001421
}
1401-
// (unsigned)x / -1 ==> x == -1
1402-
// TODO: i64 as well if sign-extension is enabled
1403-
if (matches(curr, binary(DivUInt32, any(), ival(-1)))) {
1404-
curr->op = Abstract::getBinary(type, Abstract::Eq);
1405-
return curr;
1406-
}
14071422
// x * -1 ==> 0 - x
14081423
if (matches(curr, binary(Abstract::Mul, any(&left), ival(-1)))) {
14091424
right->value = Literal::makeZero(type);

test/passes/optimize-instructions_all-features.txt

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3947,9 +3947,51 @@
39473947
)
39483948
)
39493949
(drop
3950-
(i64.div_u
3950+
(i64.extend_i32_u
3951+
(i64.eq
3952+
(local.get $y)
3953+
(i64.const -1)
3954+
)
3955+
)
3956+
)
3957+
)
3958+
(func $rhs-is-neg-const (param $x i32) (param $y i64)
3959+
(drop
3960+
(i32.ge_u
3961+
(local.get $x)
3962+
(i32.const -2)
3963+
)
3964+
)
3965+
(drop
3966+
(i32.eq
3967+
(local.get $x)
3968+
(i32.const -1)
3969+
)
3970+
)
3971+
(drop
3972+
(i32.ge_u
3973+
(local.get $x)
3974+
(i32.const -2147483647)
3975+
)
3976+
)
3977+
(drop
3978+
(i32.shr_u
3979+
(local.get $x)
3980+
(i32.const 31)
3981+
)
3982+
)
3983+
(drop
3984+
(i64.extend_i32_u
3985+
(i64.eq
3986+
(local.get $y)
3987+
(i64.const -1)
3988+
)
3989+
)
3990+
)
3991+
(drop
3992+
(i64.shr_u
39513993
(local.get $y)
3952-
(i64.const -1)
3994+
(i64.const 63)
39533995
)
39543996
)
39553997
)

test/passes/optimize-instructions_all-features.wast

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4452,6 +4452,38 @@
44524452
(i64.const -1)
44534453
))
44544454
)
4455+
(func $rhs-is-neg-const (param $x i32) (param $y i64)
4456+
;; u32(x) / -2 => x >= -2
4457+
(drop (i32.div_u
4458+
(local.get $x)
4459+
(i32.const -2)
4460+
))
4461+
;; u32(x) / -1 => x == -1
4462+
(drop (i32.div_u
4463+
(local.get $x)
4464+
(i32.const -1)
4465+
))
4466+
;; u32(x) / (i32.min + 1)
4467+
(drop (i32.div_u
4468+
(local.get $x)
4469+
(i32.const -2147483647)
4470+
))
4471+
;; u32(x) / i32.min => x >>> 31
4472+
(drop (i32.div_u
4473+
(local.get $x)
4474+
(i32.const -2147483648)
4475+
))
4476+
;; u64(x) / -1 => u64(x == -1)
4477+
(drop (i64.div_u
4478+
(local.get $y)
4479+
(i64.const -1)
4480+
))
4481+
;; u64(x) / i64.min => x >>> 63
4482+
(drop (i64.div_u
4483+
(local.get $y)
4484+
(i64.const -9223372036854775808)
4485+
))
4486+
)
44554487
(func $pre-combine-or (param $x i32) (param $y i32)
44564488
(drop (i32.or
44574489
(i32.gt_s

0 commit comments

Comments
 (0)