Skip to content

Commit 2f902f7

Browse files
committed
optimize: pow when base is a power of two
if base == 2 ** k, then (2 ** k) ** n == 2 ** (k * n) == 1 << (k * n)
1 parent 07249ab commit 2f902f7

File tree

4 files changed

+76
-7
lines changed

4 files changed

+76
-7
lines changed

library/core/src/num/uint_macros.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2085,11 +2085,23 @@ macro_rules! uint_impl {
20852085
without modifying the original"]
20862086
#[inline]
20872087
pub const fn checked_pow(self, mut exp: u32) -> Option<Self> {
2088+
let mut base = self;
2089+
let mut acc: Self = 1;
2090+
2091+
if intrinsics::is_val_statically_known(base) && base.is_power_of_two() {
2092+
// change of base:
2093+
// if base == 2 ** k, then
2094+
// (2 ** k) ** n
2095+
// == 2 ** (k * n)
2096+
// == 1 << (k * n)
2097+
let k = base.ilog2();
2098+
let shift = try_opt!(k.checked_mul(exp));
2099+
return (1 as Self).checked_shl(shift);
2100+
}
2101+
20882102
if exp == 0 {
20892103
return Some(1);
20902104
}
2091-
let mut base = self;
2092-
let mut acc: Self = 1;
20932105

20942106
if intrinsics::is_val_statically_known(exp) {
20952107
while exp > 1 {
@@ -3255,14 +3267,28 @@ macro_rules! uint_impl {
32553267
without modifying the original"]
32563268
#[inline]
32573269
pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) {
3258-
if exp == 0 {
3259-
return (1, false);
3260-
}
32613270
let mut base = self;
32623271
let mut acc: Self = 1;
32633272
let mut overflow = false;
32643273
let mut tmp_overflow;
32653274

3275+
if intrinsics::is_val_statically_known(base) && base.is_power_of_two() {
3276+
// change of base:
3277+
// if base == 2 ** k, then
3278+
// (2 ** k) ** n
3279+
// == 2 ** (k * n)
3280+
// == 1 << (k * n)
3281+
let k = base.ilog2();
3282+
let Some(shift) = k.checked_mul(exp) else {
3283+
return (0, true)
3284+
};
3285+
return ((1 as Self).unbounded_shl(shift), shift >= Self::BITS)
3286+
}
3287+
3288+
if exp == 0 {
3289+
return (1, false);
3290+
}
3291+
32663292
if intrinsics::is_val_statically_known(exp) {
32673293
while exp > 1 {
32683294
if (exp & 1) == 1 {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//@ compile-flags: -Copt-level=3
2+
// Test that `pow` can use a faster implementation when `base` is a
3+
// known power of two
4+
5+
#![crate_type = "lib"]
6+
7+
// CHECK-LABEL: @pow2
8+
#[no_mangle]
9+
pub fn pow2(exp: u32) -> u32 {
10+
// CHECK: %[[OVERFLOW:.+]] = icmp ugt i32 %exp, 31
11+
// CHECK: %[[POW:.+]] = shl nuw i32 1, %exp
12+
// CHECK: %[[RET:.+]] = select i1 %[[OVERFLOW]], i32 0, i32 %[[POW]]
13+
// CHECK: ret i32 %[[RET]]
14+
2u32.pow(exp)
15+
}
16+
17+
// 4 ** n == 2 ** (2 * n) == 1 << (2 * n)
18+
// CHECK-LABEL: @pow4
19+
#[no_mangle]
20+
pub fn pow4(exp: u32) -> u32 {
21+
// CHECK: %[[SHIFT_AMOUNT:.+]] = shl i32 %exp, 1
22+
// CHECK: %[[ICMP1:.+]] = icmp slt i32 %exp, 0
23+
// CHECK: %[[ICMP2:.+]] = icmp ugt i32 %[[SHIFT_AMOUNT]], 31
24+
// CHECK: %[[OVERFLOW:.+]] = or i1 %[[ICMP1]], %[[ICMP2]]
25+
// CHECK: %[[POW:.+]] = shl nuw i32 1, %[[SHIFT_AMOUNT]]
26+
// CHECK: %[[RET:.+]] = select i1 %[[OVERFLOW]], i32 0, i32 %[[POW]]
27+
// CHECK: ret i32 %[[RET]]
28+
4u32.pow(exp)
29+
}
30+
31+
// 16 ** n == 2 ** (4 * n) == 1 << (4 * n)
32+
// CHECK-LABEL: @pow16
33+
#[no_mangle]
34+
pub fn pow16(exp: u32) -> u32 {
35+
// CHECK: %[[SHIFT_AMOUNT:.+]] = shl i32 %exp, 2
36+
// CHECK: %[[ICMP1:.+]] = icmp ugt i32 %exp, 1073741823
37+
// CHECK: %[[ICMP2:.+]] = icmp ugt i32 %[[SHIFT_AMOUNT]], 31
38+
// CHECK: %[[OVERFLOW:.+]] = or i1 %[[ICMP1]], %[[ICMP2]]
39+
// CHECK: %[[POW:.+]] = shl nuw i32 1, %[[SHIFT_AMOUNT]]
40+
// CHECK: %[[RET:.+]] = select i1 %[[OVERFLOW]], i32 0, i32 %[[POW]]
41+
// CHECK: ret i32 %[[RET]]
42+
16u32.pow(exp)
43+
}

tests/ui/numbers-arithmetic/overflowing-pow-signed.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ run-fail
22
//@ regex-error-pattern: thread 'main'.*panicked
3-
//@ error-pattern: attempt to multiply with overflow
3+
//@ regex-error-pattern: attempt to (multiply|shift left) with overflow
44
//@ needs-subprocess
55
//@ compile-flags: -C debug-assertions
66

tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@ run-fail
22
//@ regex-error-pattern: thread 'main'.*panicked
3-
//@ error-pattern: attempt to multiply with overflow
3+
//@ regex-error-pattern: attempt to (multiply|shift left) with overflow
44
//@ needs-subprocess
55
//@ compile-flags: -C debug-assertions
66

0 commit comments

Comments
 (0)