Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions library/core/src/num/uint_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1571,9 +1571,14 @@ macro_rules! uint_impl {
// applied by the compiler. If you want those specific bases,
// use `.checked_ilog2()` or `.checked_ilog10()` directly.
if core::intrinsics::is_val_statically_known(base) {
if base == 2 {
return self.checked_ilog2();
} else if base == 10 {
// change of base:
// if base == 2 ** k, then
// log(base, n) == log(2, n) / k
if base.is_power_of_two() && base > 1 {
let k = base.ilog2();
return Some(try_opt!(self.checked_ilog2()) / k);
}
if base == 10 {
return self.checked_ilog10();
}
}
Expand Down Expand Up @@ -3309,6 +3314,18 @@ macro_rules! uint_impl {
let mut base = self;
let mut acc = 1;

if intrinsics::is_val_statically_known(base) {
// change of base:
// if base == 2 ** k, then
// (2 ** k) ** n
// == 2 ** (k * n)
// == 1 << (k * n)
if base.is_power_of_two() {
let k = base.ilog2();
return 1 << (k * exp)
}
}

if intrinsics::is_val_statically_known(exp) {
while exp > 1 {
if (exp & 1) == 1 {
Expand Down
36 changes: 36 additions & 0 deletions tests/codegen-llvm/ilog_known_base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//@ compile-flags: -Copt-level=3
// Test that `checked_ilog` can use a faster implementation when `base` is a
// known power of two

#![crate_type = "lib"]

// CHECK-LABEL: @checked_ilog2
#[no_mangle]
pub fn checked_ilog2(val: u32) -> Option<u32> {
// CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0
// CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true)
// CHECK: %[[LOG2:.+]] = xor i32 %[[CTZ]], 31
val.checked_ilog(2)
}

// log(4, x) == log(2, x) / 2
// CHECK-LABEL: @checked_ilog4
#[no_mangle]
pub fn checked_ilog4(val: u32) -> Option<u32> {
// CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0
// CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true)
// CHECK: %[[DIV2:.+]] = lshr i32 %[[CTZ]], 1
// CHECK: %[[LOG4:.+]] = xor i32 %[[DIV2]], 15
val.checked_ilog(4)
}

// log(16, x) == log(2, x) / 4
// CHECK-LABEL: @checked_ilog16
#[no_mangle]
pub fn checked_ilog16(val: u32) -> Option<u32> {
// CHECK: %[[ICMP:.+]] = icmp ne i32 %val, 0
// CHECK: %[[CTZ:.+]] = tail call range(i32 0, 33) i32 @llvm.ctlz.i32(i32 %val, i1 true)
// CHECK: %[[DIV4:.+]] = lshr i32 %[[CTZ]], 2
// CHECK: %[[LOG16:.+]] = xor i32 %[[DIV2]], 7
val.checked_ilog(16)
}
36 changes: 36 additions & 0 deletions tests/codegen-llvm/pow_known_base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//@ compile-flags: -Copt-level=3
// Test that `pow` can use a faster implementation when `base` is a
// known power of two

#![crate_type = "lib"]

// CHECK-LABEL: @pow2
#[no_mangle]
pub fn pow2(exp: u32) -> u32 {
// CHECK: %[[SHIFT_AMOUNT:.+]] = and i32 %exp, 31
// CHECK: %[[POW2:.+]] = shl nuw i32 1, %[[SHIFT_AMOUNT]]
// CHECK: ret i32 %[[POW2]]
2u32.pow(exp)
}

// 4 ** n == 2 ** (2 * n) == 1 << (2 * n)
// CHECK-LABEL: @pow4
#[no_mangle]
pub fn pow4(exp: u32) -> u32 {
// CHECK: %[[EXP2:.+]] = shl i32 %exp, 1
// CHECK: %[[SHIFT_AMOUNT:.+]] = and i32 %[[EXP2]], 30
// CHECK: %[[POW4:.+]] = shl nuw nsw i32 1, %[[SHIFT_AMOUNT]]
// CHECK: ret i32 %[[POW4]]
4u32.pow(exp)
}

// 16 ** n == 2 ** (4 * n) == 1 << (4 * n)
// CHECK-LABEL: @pow16
#[no_mangle]
pub fn pow16(exp: u32) -> u32 {
// CHECK: %[[EXP2:.+]] = shl i32 %exp, 2
// CHECK: %[[SHIFT_AMOUNT:.+]] = and i32 %[[EXP2]], 28
// CHECK: %[[POW16:.+]] = shl nuw nsw i32 1, %[[SHIFT_AMOUNT]]
// CHECK: ret i32 %[[POW16]]
16u32.pow(exp)
}
Loading