CPU Architecture Awareness
CPU architecture awareness allows systems programs to take advantage of hardware-specific SIMD vector units, bit-manipulation instructions, cryptographic accelerators, and inline assembly (core::arch) while maintaining a portable scalar fallback so the same crate compiles and runs cleanly on x86_64, aarch64, riscv64, and wasm32.
Architecture-Aware Dispatch in Rust:
1. Portable Bit Intrinsics (`u64::count_ones`, `leading_zeros`, `rotate_left`)
---> Automatically lowers to `popcnt` / `lzcnt` / `rbit` on target CPU!
2. Compile-Time Selection (`#[cfg(target_arch = "...")]` + `#[target_feature]`)
---> Specialized path selected when compiled with `-C target-cpu=native`
3. Runtime CPU Feature Detection (`std::arch::is_x86_feature_detected!("avx2")`)
---> Queries CPUID / OS auxiliary vector at runtime and dispatches dynamically!
1. Portable Hardware Intrinsics on Primitive Integers
Before reaching for architecture-specific core::arch modules, note that Rust's primitive integer types (u8..u128, usize) expose methods that compile directly into single-cycle CPU instructions when available:
| Rust Integer Method | x86_64 Instruction |
aarch64 (ARM64) Instruction |
Purpose |
|---|---|---|---|
x.count_ones() |
popcnt |
cnt + addv |
Counts set 1 bits (Hamming weight). |
x.leading_zeros() |
lzcnt / bsr |
clz |
Counts leading 0 bits from MSB (used for fast $\lfloor\log_2(x)\rfloor$). |
x.trailing_zeros() |
tzcnt / bsf |
rbit + clz |
Counts trailing 0 bits from LSB (fast bitset iteration). |
x.swap_bytes() |
bswap |
rev |
Reverses byte order (endianness conversion). |
x.reverse_bits() |
Bitwise mask sequence | rbit |
Reverses bit order within the integer. |
x.rotate_left(k) |
rol |
ror |
Circular bit rotation (cryptographic hashes/ciphers). |
2. Compile-Time vs Runtime CPU Feature Dispatch (core::arch)
When writing SIMD or hardware-accelerated routines using core::arch::x86_64 or core::arch::aarch64, you can combine #[target_feature(enable = "...")] with runtime feature detection macros (is_x86_feature_detected! or std::arch::is_aarch64_feature_detected!) so binaries built for baseline targets still use hardware acceleration on modern CPUs:
pub fn dot_product_u32(a: &[u32], b: &[u32]) -> u64 {
assert_eq!(a.len(), b.len());
#[cfg(target_arch = "x86_64")]
{
if std::arch::is_x86_feature_detected!("avx2") {
// SAFETY: Verified `avx2` is supported on the current CPU at runtime.
return unsafe { dot_product_avx2_enabled(a, b) };
}
}
#[cfg(target_arch = "aarch64")]
{
if std::arch::is_aarch64_feature_detected!("neon") {
// SAFETY: Verified `neon` is supported on the current ARM64 CPU.
return unsafe { dot_product_neon_enabled(a, b) };
}
}
dot_product_scalar(a, b)
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn dot_product_avx2_enabled(a: &[u32], b: &[u32]) -> u64 {
// Compiled by LLVM with AVX2 vectorization enabled specifically for this function!
dot_product_scalar(a, b)
}
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
unsafe fn dot_product_neon_enabled(a: &[u32], b: &[u32]) -> u64 {
// Compiled by LLVM with ARMv8 NEON vectorization enabled!
dot_product_scalar(a, b)
}
fn dot_product_scalar(a: &[u32], b: &[u32]) -> u64 {
a.iter()
.zip(b.iter())
.map(|(&x, &y)| u64::from(x) * u64::from(y))
.sum()
}
fn main() {
let left = [10u32, 20, 30, 40];
let right = [2u32, 3, 4, 5];
let total = dot_product_u32(&left, &right);
assert_eq!(total, 20 + 60 + 120 + 200);
println!(
"Dot product on {} = {total} (popcnt={})",
std::env::consts::ARCH,
total.count_ones()
);
}
Dot product on aarch64 = 400 (popcnt=3)
3. Inline Assembly (core::arch::asm!)
For low-level OS kernels, bootloaders, or reading CPU control/cycle registers (rdtsc on x86_64, cntvct_el0 on ARM64), Rust provides stable inline assembly via core::arch::asm! with explicit register constraints and clobber tracking.