Skip to content

Commit

Permalink
Rollup merge of #81107 - scottmcm:nonzero-is_power_of_two, r=kennytm
Browse files Browse the repository at this point in the history
Add NonZeroUn::is_power_of_two

This saves instructions on both new and old machines <https://rust.godbolt.org/z/4fjTMz>
- On the default x64 target (with no fancy instructions available) it saves a few instructions by not needing to also check for zero.
- On newer targets (with BMI1) it uses `BLSR` for super-short assembly.

This can be used for things like checks against alignments stored in `NonZeroUsize`.
  • Loading branch information
m-ou-se authored Jan 17, 2021
2 parents 24b606d + 3e16e92 commit 94ec9f9
Showing 1 changed file with 40 additions and 0 deletions.
40 changes: 40 additions & 0 deletions library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,43 @@ nonzero_integers_div! {
NonZeroU128(u128);
NonZeroUsize(usize);
}

macro_rules! nonzero_unsigned_is_power_of_two {
( $( $Ty: ident )+ ) => {
$(
impl $Ty {

/// Returns `true` if and only if `self == (1 << k)` for some `k`.
///
/// On many architectures, this function can perform better than `is_power_of_two()`
/// on the underlying integer type, as special handling of zero can be avoided.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(nonzero_is_power_of_two)]
///
#[doc = concat!("let eight = std::num::", stringify!($Ty), "::new(8).unwrap();")]
/// assert!(eight.is_power_of_two());
#[doc = concat!("let ten = std::num::", stringify!($Ty), "::new(10).unwrap();")]
/// assert!(!ten.is_power_of_two());
/// ```
#[unstable(feature = "nonzero_is_power_of_two", issue = "81106")]
#[inline]
pub const fn is_power_of_two(self) -> bool {
// LLVM 11 normalizes `unchecked_sub(x, 1) & x == 0` to the implementation seen here.
// On the basic x86-64 target, this saves 3 instructions for the zero check.
// On x86_64 with BMI1, being nonzero lets it codegen to `BLSR`, which saves an instruction
// compared to the `POPCNT` implementation on the underlying integer type.

intrinsics::ctpop(self.get()) < 2
}

}
)+
}
}

nonzero_unsigned_is_power_of_two! { NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize }

0 comments on commit 94ec9f9

Please sign in to comment.