The intrinsics namespace declares operations recognized directly by the
compiler or runtime. Most exist to implement the prelude and should not appear
in ordinary application code.
Almost every entry is unsafe and needs an enclosing unsafe { ... }. The
exceptions are the layout and classification queries — size_of, align_of,
offset_of, is_rc_managed, row_fingerprint — plus cmp, num_to_float,
yield_now, and sleep.
Memory
alloc returns an arena-relative ptr[u8]. ptr_cast gives it a pointee
type, store and load move values through it, and dealloc returns the block
using the same size and alignment that allocated it.
fn main(): void {
size := intrinsics.size_of[int]()
align := intrinsics.align_of[int]()
value := unsafe {
block := intrinsics.alloc(size, align)
slot := intrinsics.ptr_cast[u8, int](block)
intrinsics.store[int](slot, 42)
read := intrinsics.load[int](slot)
intrinsics.dealloc(block, size, align)
read
}
println("value ${value}")
}realloc resizes a block, in place when it can. The new size is part of the
layout contract, so it is what a later dealloc must be given:
fn main(): void {
size := intrinsics.size_of[int]()
align := intrinsics.align_of[int]()
value := unsafe {
block := intrinsics.alloc(size, align)
bigger := intrinsics.realloc(block, size, size * 2, align)
slot := intrinsics.ptr_cast[u8, int](bigger)
intrinsics.store[int](slot, 7)
read := intrinsics.load[int](slot)
intrinsics.dealloc(bigger, size * 2, align)
read
}
println("value ${value}")
}Sizes and alignments are usize. A typed load or store still needs an
initialized, aligned range large enough for T; passing the type checker proves
none of that.
Pointers
ptr[T] is arena-relative and participates in the compiler’s guest-memory
model. rawptr[T] is an absolute WebAssembly address obtained with to_raw,
and it is what the bulk memory operations take. raw_add advances a raw
pointer by a byte count and raw_cast retypes one.
fn main(): void {
len := 8usize
align := intrinsics.align_of[u32]()
copied := unsafe {
source := intrinsics.alloc(len, align)
target := intrinsics.alloc(len, align)
intrinsics.mem_fill(intrinsics.to_raw[u8](source), 0x7Fu8, len)
intrinsics.mem_copy(intrinsics.to_raw[u8](target), intrinsics.to_raw[u8](source), len)
read := intrinsics.load[u32](intrinsics.ptr_cast[u8, u32](target))
intrinsics.dealloc(target, len, align)
intrinsics.dealloc(source, len, align)
read
}
println("copied ${copied}")
}mem_copy requires non-overlapping ranges; use mem_move when they can
overlap. mem_fill writes a repeated byte, not a typed default.
A rawptr may not be live across a suspension point, because the runtime can
move arena state while execution is yielded. The compiler enforces this at
lowering time, so a program can pass atoll check and still be rejected by
atoll build:
fn main(): void {
len := 8usize
align := intrinsics.align_of[u32]()
unsafe {
block := intrinsics.alloc(len, align)
raw := intrinsics.to_raw[u8](block)
println("logging mid-region")
intrinsics.mem_fill(raw, 0u8, len)
intrinsics.dealloc(block, len, align)
}
}ATOLL4011: rawptr value is live across a suspend point. Finish the raw work,
produce a managed value, then do the I/O. Converting between ptr and rawptr
is a view change, never an ownership transfer.
Layout
Layout queries are safe to call and fold after generic specialization.
struct Packet { id: u32, payload: u64 }
fn main(): void {
size := intrinsics.size_of[Packet]()
align := intrinsics.align_of[Packet]()
offset := intrinsics.offset_of[Packet]("payload")
println("size ${size} align ${align} offset ${offset}")
}offset_of requires a real field of the specialized type. The results are
backend ABI facts, not a portable serialization format: do not persist a raw
struct image and assume another target or a later compiler lays it out the same
way.
row_fingerprint[T] supports typed dataframe result registration.
Ownership
is_rc_managed[T] exposes the compiler’s representation classification, which
is what prelude code branches on before touching reference counts.
struct Point { x: int, y: int }
fn main(): void {
println("int ${intrinsics.is_rc_managed[int]()}")
println("string ${intrinsics.is_rc_managed[string]()}")
println("list ${intrinsics.is_rc_managed[[]int]()}")
println("point ${intrinsics.is_rc_managed[Point]()}")
}The retain, release, clone, and drop operations beneath that classification exist for compiler and prelude implementations:
retain[T]adds another managed ownership claim;shallow_releaseremoves one claim and can recursively destroy children;drop_in_place[T]drops a value at a known initialized location;is_uniquereports whether a handle has exactly one claim;- raw stores express no high-level replacement semantics at all.
Application code should rely on ordinary assignment, arguments, return values, and scoped cleanup. Mixing these intrinsics with compiler-generated ownership leaks, double-releases, or uses freed storage.
Numeric and SIMD
The namespace exposes scalar numeric primitives and the WebAssembly v128
surface: lane splats, extraction and replacement, arithmetic, comparisons,
shifts, bitwise operations, float operations, and selected conversions.
fn main(): void {
mask := unsafe {
lane := intrinsics.i8x16_splat(3u8)
intrinsics.i8x16_bitmask(intrinsics.i8x16_eq(lane, lane))
}
println("mask ${mask}")
}That prints 65535: sixteen equal lanes set sixteen mask bits. These names
mirror backend instructions rather than portable algorithms, so wrap them behind
a feature-appropriate API and keep a scalar path where the target contract needs
one. Lane types, wrapping, signedness, and conversion semantics follow the named
WebAssembly instruction, not the Numeric trait. v128_load and v128_store
take raw pointers and therefore inherit the suspension rule above.
Bit reinterpretation
transmute[F, T] reinterprets a value’s bits between compiler-approved
layouts. Same-width integer reinterpretation lowers today:
fn main(): void {
unsigned := unsafe { intrinsics.transmute[i32, u32](7i32) }
signed := unsafe { intrinsics.transmute[u32, i32](unsigned) }
println("${unsigned} ${signed}")
}| Form | Status |
|---|---|
transmute[i32, u32], transmute[u32, i32] |
Lowers |
transmute[f32, u32], transmute[f64, u64] and the reverse |
Declared in the prelude, but has no lowering path — the build fails with WasmIR could not lower continuation |
Use the float type’s own conversion methods until float/integer bit casts lower.
Runtime plumbing
Scheduler, task, stream, reference-count, and host-ABI intrinsics support standard-library implementation. Their handle encodings and frame offsets are not stable application interfaces. Several are marked private in the prelude; others stay name-resolvable only because standard-library bodies need them, and that visibility is not a supported user contract.
Portability
Before exposing an intrinsic through application code, decide which layer owns the dependency:
| Need | Preferred layer |
|---|---|
| Safe reusable operation | Standard-library or project wrapper |
| Target-specific optimization | Small tested unsafe module |
| Compiler representation query | Compiler/prelude implementation |
| Host capability | Typed @host adapter |
Read Unsafe before calling any unsafe intrinsic. Passing the type checker is not proof that a low-level invariant holds.