diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index d68c7be9865b1..3184d13d25cfe 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -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(); } } @@ -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 { diff --git a/tests/codegen-llvm/ilog_known_base.rs b/tests/codegen-llvm/ilog_known_base.rs new file mode 100644 index 0000000000000..3f0cdd551c28b --- /dev/null +++ b/tests/codegen-llvm/ilog_known_base.rs @@ -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 { + // 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 { + // 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 { + // 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) +} diff --git a/tests/codegen-llvm/pow_known_base.rs b/tests/codegen-llvm/pow_known_base.rs new file mode 100644 index 0000000000000..34a56a74c848d --- /dev/null +++ b/tests/codegen-llvm/pow_known_base.rs @@ -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) +}