macro_rules! opt_tzcnt_for {
($num:ty, $name:ident, $reg:expr) => {
#[inline(always)]
pub fn $name(n: $num) -> u32 {
#[cfg(all(
not(miri), // MIRI doesn't support inline assembly
not(target_feature="bmi1"), // else the compiler will just generate a `tzcnt` without branching, let it be
target_arch = "x86_64",
))]
{
let x = n;
let mut tz: $num = <$num>::BITS as $num;
unsafe {
core::arch::asm!(
concat!("rep bsf {tz:", $reg, "}, {x:", $reg, "}"),
x = in(reg) x,
tz = inlateout(reg) tz,
options(pure, nomem, nostack)
);
}
return tz as u32;
}
#[allow(unreachable_code)]
n.trailing_zeros()
}
};
}
opt_tzcnt_for!(u32, tzcnt_u32, "e");
#[cfg(target_pointer_width = "64")]
opt_tzcnt_for!(u64, tzcnt_u64, "r");
#[cfg(not(target_pointer_width = "64"))]
pub fn tzcnt_u64(n: u64) -> u32 {
n.trailing_zeros()
}
#[cfg(target_pointer_width = "64")]
opt_tzcnt_for!(usize, tzcnt_usize, "r");
#[cfg(target_pointer_width = "32")]
opt_tzcnt_for!(usize, tzcnt_usize, "e");