Skip to content

Commit 6dc9604

Browse files
committed
Optimize checked_ilog when base is a power of two
if base == 2 ** k, then log(base, n) == log(2, n) / k
1 parent 4056082 commit 6dc9604

File tree

2 files changed

+44
-3
lines changed

2 files changed

+44
-3
lines changed

library/core/src/num/uint_macros.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,9 +1571,14 @@ macro_rules! uint_impl {
15711571
// applied by the compiler. If you want those specific bases,
15721572
// use `.checked_ilog2()` or `.checked_ilog10()` directly.
15731573
if core::intrinsics::is_val_statically_known(base) {
1574-
if base == 2 {
1575-
return self.checked_ilog2();
1576-
} else if base == 10 {
1574+
// change of base:
1575+
// if base == 2 ** k, then
1576+
// log(base, n) == log(2, n) / k
1577+
if base.is_power_of_two() && base > 1 {
1578+
let k = base.ilog2();
1579+
return Some(try_opt!(self.checked_ilog2()) / k);
1580+
}
1581+
if base == 10 {
15771582
return self.checked_ilog10();
15781583
}
15791584
}
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 `checked_ilog` can use a faster implementation when `base` is a
3+
// known power of two
4+
5+
#![crate_type = "lib"]
6+
7+
// CHECK-LABEL: @checked_ilog2
8+
#[no_mangle]
9+
pub fn checked_ilog2(val: u32) -> Option<u32> {
10+
// CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0
11+
// CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true)
12+
// CHECK: %[[LOG2:.+]] = xor i32 %[[CTZ]], 31
13+
val.checked_ilog(2)
14+
}
15+
16+
// log(4, x) == log(2, x) / 2
17+
// CHECK-LABEL: @checked_ilog4
18+
#[no_mangle]
19+
pub fn checked_ilog4(val: u32) -> Option<u32> {
20+
// CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0
21+
// CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true)
22+
// CHECK: %[[DIV2:.+]] = lshr i32 %[[CTZ]], 1
23+
// CHECK: %[[LOG4:.+]] = xor i32 %[[DIV2]], 15
24+
val.checked_ilog(4)
25+
}
26+
27+
// log(16, x) == log(2, x) / 4
28+
// CHECK-LABEL: @checked_ilog16
29+
#[no_mangle]
30+
pub fn checked_ilog16(val: u32) -> Option<u32> {
31+
// CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0
32+
// CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true)
33+
// CHECK: %[[DIV4:.+]] = lshr i32 %[[CTZ]], 2
34+
// CHECK: %[[LOG16:.+]] = xor i32 %[[DIV2]], 7
35+
val.checked_ilog(16)
36+
}

0 commit comments

Comments
 (0)