Skip to content

Commit 9726f4d

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 c797096 commit 9726f4d

File tree

2 files changed

+48
-3
lines changed

2 files changed

+48
-3
lines changed

library/core/src/num/uint_macros.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,10 @@ macro_rules! uint_impl {
15671567
///
15681568
/// ```
15691569
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_ilog(5), Some(1));")]
1570+
#[doc = concat!("assert_eq!(4", stringify!($SelfT), ".checked_ilog(5), Some(0));")]
1571+
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_ilog(0), None);")]
1572+
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".checked_ilog(1), None);")]
1573+
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".checked_ilog(1), None);")]
15701574
/// ```
15711575
#[stable(feature = "int_log", since = "1.67.0")]
15721576
#[rustc_const_stable(feature = "int_log", since = "1.67.0")]
@@ -1581,9 +1585,14 @@ macro_rules! uint_impl {
15811585
// applied by the compiler. If you want those specific bases,
15821586
// use `.checked_ilog2()` or `.checked_ilog10()` directly.
15831587
if core::intrinsics::is_val_statically_known(base) {
1584-
if base == 2 {
1585-
return self.checked_ilog2();
1586-
} else if base == 10 {
1588+
// change of base:
1589+
// if base == 2 ** k, then
1590+
// log(base, n) == log(2, n) / k
1591+
if base.is_power_of_two() && base > 1 {
1592+
let k = base.ilog2();
1593+
return Some(try_opt!(self.checked_ilog2()) / k);
1594+
}
1595+
if base == 10 {
15871596
return self.checked_ilog10();
15881597
}
15891598
}
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)