Skip to content

Commit 53449c9

Browse files
committed
Optimize pow when self is a power of two
if base == 2 ** k, then (2 ** k) ** n == 2 ** (k * n) == 1 << (k * n)
1 parent ee9dd66 commit 53449c9

File tree

5 files changed

+56
-2
lines changed

5 files changed

+56
-2
lines changed

library/core/src/num/uint_macros.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3318,6 +3318,18 @@ macro_rules! uint_impl {
33183318
let mut base = self;
33193319
let mut acc = 1;
33203320

3321+
if intrinsics::is_val_statically_known(base) {
3322+
// change of base:
3323+
// if base == 2 ** k, then
3324+
// (2 ** k) ** n
3325+
// == 2 ** (k * n)
3326+
// == 1 << (k * n)
3327+
if base.is_power_of_two() {
3328+
let k = base.ilog2();
3329+
return 1 << (k * exp)
3330+
}
3331+
}
3332+
33213333
if intrinsics::is_val_statically_known(exp) {
33223334
while exp > 1 {
33233335
if (exp & 1) == 1 {

library/coretests/tests/num/uint_macros.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,12 @@ macro_rules! uint_module {
271271
assert_eq!(from_str::<$T>("x"), None);
272272
}
273273

274+
#[test]
275+
#[should_panic]
276+
fn test_pow_overflow() {
277+
let _ = <$T>::pow(2, 129);
278+
}
279+
274280
test_runtime_and_compiletime! {
275281
fn test_parse_bytes() {
276282
assert_eq_const_safe!(Result<$T, ParseIntError>: $T::from_str_radix("123", 10), Ok(123 as $T));
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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: %[[SHIFT_AMOUNT:.+]] = and i32 %exp, 31
11+
// CHECK: %[[POW2:.+]] = shl nuw i32 1, %[[SHIFT_AMOUNT]]
12+
// CHECK: ret i32 %[[POW2]]
13+
2u32.pow(exp)
14+
}
15+
16+
// 4 ** n == 2 ** (2 * n) == 1 << (2 * n)
17+
// CHECK-LABEL: @pow4
18+
#[no_mangle]
19+
pub fn pow4(exp: u32) -> u32 {
20+
// CHECK: %[[EXP2:.+]] = shl i32 %exp, 1
21+
// CHECK: %[[SHIFT_AMOUNT:.+]] = and i32 %[[EXP2]], 30
22+
// CHECK: %[[POW4:.+]] = shl nuw nsw i32 1, %[[SHIFT_AMOUNT]]
23+
// CHECK: ret i32 %[[POW4]]
24+
4u32.pow(exp)
25+
}
26+
27+
// 16 ** n == 2 ** (4 * n) == 1 << (4 * n)
28+
// CHECK-LABEL: @pow16
29+
#[no_mangle]
30+
pub fn pow16(exp: u32) -> u32 {
31+
// CHECK: %[[EXP2:.+]] = shl i32 %exp, 2
32+
// CHECK: %[[SHIFT_AMOUNT:.+]] = and i32 %[[EXP2]], 28
33+
// CHECK: %[[POW16:.+]] = shl nuw nsw i32 1, %[[SHIFT_AMOUNT]]
34+
// CHECK: ret i32 %[[POW16]]
35+
16u32.pow(exp)
36+
}

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)