std.cpu and std.intrinsic
API summary (generated from the Beans source by npm run coverage): 18 package functions · 1 type.
These two native modules give you low-level CPU access. std.cpu asks the machine
which instruction-set features it has and gates code on them. std.intrinsic
exposes a small, fixed set of hardware and collector-control operations. Both are typed in the checker,
so their functions are positional and carry no parameter names.
std.cpu
Section titled “std.cpu”import std.cpuhas(CpuFeature) -> boolhas_name(string) -> boolhas(CpuFeature.x)asks whether the running machine has featurex.has_name(name)asks the same question by feature name string.
cpu.has asks the machine your program is actually running on, not the build
target. CpuFeature is not a fixed enum: the valid selectors are the feature
names of the selected target, and they are checked at the call site.
Feature names by architecture:
- x86-64:
sse2,sse3,ssse3,sse4_1,sse4_2,popcnt,avx,avx2,fma,bmi,bmi2,f16c,aes,pclmul,avx512f. Names with a dot in the ISA (likesse4.1) are written with an underscore (sse4_1). - AArch64:
neon,fp16,dotprod,crc,aes.
Feature-gated functions
Section titled “Feature-gated functions”You can mark a function body as allowed to use a feature’s instructions:
feature "avx2" fn wide_add() { // may use AVX2 instructions}Calling such a function requires the feature to be known present. That is true in
three cases: inside an if cpu.has(...) guard for that feature, from another
function that already requires the feature, or in a build made with
--features +avx2.
The example uses aes, one of the few features both x86-64 and AArch64 name the
same way, so this one file compiles on either:
import std.ioimport std.cpu
feature "aes" fn fast_path() -> int { return 1}
fn main() { if cpu.has(CpuFeature.aes) { io.println("fast path {fast_path()}") } else { io.println("scalar fallback") } io.println("has aes by name {cpu.has_name("aes")}")}See the attributes guide for feature.
std.intrinsic
Section titled “std.intrinsic”import std.intrinsicThe hardware entries map to single operations. All entries require
unsafe, and the set is a closed allowlist, you cannot add to
it.
popcount(int) -> intleading_zeros(int) -> inttrailing_zeros(int) -> intbswap16(int) -> intbswap32(int) -> intbswap64(int) -> introtate_left(int, int) -> introtate_right(int, int) -> intcrc32c(int, int) -> intsqrt(float) -> floatsqrt32(f32) -> f32fma(float, float, float) -> floatfma32(f32, f32, f32) -> f32prefetch(RawPtr<u8>)spin_hint()with_collection_deferred(fn() -> unit)popcountcounts set bits.leading_zerosandtrailing_zeroscount leading and trailing zero bits; a zero input gives 64.bswap16,bswap32, andbswap64reverse byte order. The narrow forms work on the low bytes and leave the rest zero.rotate_leftandrotate_rightrotate the bits of the first argument by the second.sqrtandsqrt32are square root, forfloatandf32.fmaandfma32are fused multiply-add (a * b + c, rounded once).prefetch(ptr)hints the CPU to load a cache line.spin_hint()hints that you are in a spin loop. Neither has an observable result.crc32c(acc, x)is one CRC-32C step. It is feature-gated: it needssse4.2on x86-64 orcrcon arm64, and it is refused on 32-bit x86.
import std.ioimport std.intrinsic
fn main() { unsafe { io.println("popcount {intrinsic.popcount(255)}") // 8 io.println("sqrt {intrinsic.sqrt(16.0)}") // 4
let block: RawPtr<u8> = RawPtr.alloc(64) intrinsic.prefetch(block) block.free() intrinsic.spin_hint() io.println("hints are safe to ignore") }}Scoped collection deferral
Section titled “Scoped collection deferral”intrinsic.with_collection_deferred(body) runs a short, non-parking closure with
cycle collection deferred on both backends. Nested regions remain deferred until
the outermost one leaves, and both normal return and contained panic restore the
gate. Pending collection resumes at a later allocation rather than necessarily
on exit.
This is an unsafe obligation the checker does not prove. The body must not yield, park, block on another fiber or thread, or perform unbounded work. It is not a lock or rollback: concurrent readers still need synchronization, earlier writes survive panic, and synchronous ARC destruction or explicit calls can still run user code. Keep replaced values alive until the invariant is settled. Other workers’ local collection is unaffected; global collection uses its existing exclusion gate. The region adds no allocation or scheduling operation beyond the closure.
import std.intrinsic
fn main() { unsafe { intrinsic.with_collection_deferred(fn() { // Settle a short multi-write invariant without parking here. }) }}See also
Section titled “See also”- Unsafe guide.
- Attributes guide,
featurefunctions. - std.target, feature availability follows the
build’s
--cpu/--features.