Compiler Intrinsics
Compiler intrinsics are low-level functions built directly into the Sun compiler. They provide direct access to memory operations, type information, and other primitives that cannot be expressed in regular Sun code.
Intrinsics are unsafe operations intended for implementing standard library primitives like Vec<T> and Map<K,V>. Most user code should use the standard library instead.
An intrinsic that reads or writes memory nothing has checked must be written
inside an unsafe block; one that only computes — a size, a type check, an
address, a numeric conversion — may be used anywhere.
Unsafe Blocks lists which is which, and where
the semicolons go in the two forms the block takes.
Generic Intrinsics
These intrinsics require a type parameter <T>.
_sizeof<T>()
Returns the byte size of type T as i64.
var size = _sizeof<i32>(); // 4
var size2 = _sizeof<i64>(); // 8
class Point { x: f64; y: f64; }
var pointSize = _sizeof<Point>(); // 16 (two 8-byte floats)_init<T>(ptr, args...)
Constructs an instance of type T at the memory location pointed to by ptr, forwarding args to the constructor.
// Allocate raw memory for a Point
var mem = unsafe { _malloc(_sizeof<Point>()); };
// Construct Point at that location
unsafe { _init<Point>(mem, 3.0, 4.0); };
// mem now points to an initialized PointFor non-class types, _init is a no-op since primitives don't have constructors.
_deinit<T>(ptr)
The counterpart of _init<T>: runs T's deinit on the value at ptr, then
drops any of its fields that own something. For a payload enum it drops through
the active variant. For anything that owns nothing it is a no-op, so a
container can call it on every element without asking what T is.
// Release element i, then reuse its slot
unsafe {
_deinit<T>(_ptr_as_raw<T>(_ptr_offset(this.data, i * _sizeof<T>())));
};It releases the value without freeing the storage, which is what a container that manages its own buffer needs.
_params_of<T>
Names the parameter list a value pack stands for. It appears only after the
colon of an args... pack in a parameter list, never as a type of its own:
function make<T>(args...: _params_of<T>) raw_ptr<T> {
var size: i64 = _sizeof<T>();
var memory: raw_ptr<i8> = unsafe { _malloc(size); };
unsafe { _init<T>(memory, args...); };
return memory;
}For a class, _params_of<T> is the parameters of T's init — any overload
the call's arguments match, so make<Point>(3, 4) and make<Point>(9) each
select their own. For a lambda, it is the parameters that lambda takes. For
anything else the pack is accepted and left unchecked.
See Value Packs for the full rules.
_return_type_of<F>
The type F returns. Like _params_of<T> it is computed rather than named,
but unlike it, it is a type in its own right and goes wherever a type
annotation goes. F must be a lambda or a function type.
public function spawn<F: _Callable>(fn: F, args...: _params_of<F>)
Thread<_return_type_of<F>> {
return Thread<_return_type_of<F>>(unsafe { _spawn<F>(fn, args...); });
}Inside the template it stays symbolic — nothing can say what F returns until
a call says what F is — and it is resolved when the call is specialized.
_spawn<F>(fn, args...)
Starts an OS thread running fn with args, and returns the
raw_ptr<ThreadContext> it allocated for it. fn is a lambda or a named
function passed as a value. Arguments move into the thread; the returned value,
including a result enum, moves to the joining thread.
The trampoline pthread_create needs is built per callee signature, which
cannot be written in Sun — pthread_create wants a ptr(*)(ptr), and Sun
lambdas use the fat-pointer ABI — so this is the one part of threading the
compiler still does. Everything else is std.thread.
_thread_join<T>(ctx) and _thread_join_drop<T>(ctx)
Wait for the thread to exit, then release its context. _thread_join<T> takes
the thread's result and hands it back; _thread_join_drop<T> drops it in
place, for a handle nobody joined by hand. Both are used only by
Thread<T>.join() and Thread<T>.deinit().
_load<T>(ptr, index)
Loads an element of type T from ptr at the given element index. Equivalent to ptr[index] in C.
// Assuming data points to an array of i32
var value = unsafe { _load<i32>(data, 5); }; // Load data[5]_store<T>(ptr, index, value)
Stores value of type T at ptr[index].
// Store 42 at data[5]
unsafe { _store<i32>(data, 5, 42); };_ptr_as_raw<T>(ptr<T>)
Converts an owning ptr<T> to a non-owning raw_ptr<T> without transferring ownership. Similar to C++'s unique_ptr::get().
var owned: ptr<Point> = new Point(1.0, 2.0);
var raw: raw_ptr<Point> = _ptr_as_raw<Point>(owned);
// raw points to the same memory, but owned still manages lifetimeBe careful: the raw pointer becomes invalid if the owning pointer is freed or goes out of scope.
_is<T>(value)
Compile-time type check that returns true or false based on whether the type of value matches T. This intrinsic is always resolved at compile time — no runtime overhead.
T can be:
- A concrete type (e.g.,
i32,f64,Point) — exact type match - A type trait — checks type category (see below)
- An interface — checks if a class implements the interface
function example(x: i32, y: f64) {
_is<i32>(x); // true - exact type match
_is<i64>(x); // false - i32 is not i64
_is<_Integer>(x); // true - i32 is an integer
_is<_Float>(y); // true - f64 is a float
}Type Traits
Type traits are pseudo-types that categorize primitives:
| Trait | Matches |
|---|---|
_Integer | i8, i16, i32, i64, u8, u16, u32, u64 |
_Signed | i8, i16, i32, i64 |
_Unsigned | u8, u16, u32, u64 |
_Float | f32, f64 |
_Numeric | All integers and floats |
_Primitive | All numeric types plus bool |
_Lambda | Any closure type |
_Function | A named-function value (function (Args) _Result) |
_Callable | _Lambda and _Function |
Use in Generic Code
_is<T> is particularly useful in generic functions to branch based on type:
function processValue<T>(x: T) i32 {
if (_is<_Integer>(x)) {
// Integer-specific logic
return 1;
}
if (_is<_Float>(x)) {
// Float-specific logic
return 2;
}
return 0;
}
function main() i32 {
var a = processValue<i32>(42); // Returns 1
var b = processValue<f64>(3.14); // Returns 2
return a + b; // 3
}Since Sun uses monomorphization, each instantiation of processValue compiles to code with the dead branches eliminated by LLVM.
Interface Checks
For class types, _is<T> can check interface implementation:
interface IHashable {
method hash() i64;
}
class MyKey implements IHashable {
method hash() i64 { return 42; }
}
function example(key: MyKey) bool {
return _is<IHashable>(key); // true
}_convert<T>(value)
Converts a numeric value to another numeric type T, unchecked. Integers
truncate or extend (sign-extending from a signed source, zero-extending from an
unsigned one); integer-to-float and float-to-integer convert by value, dropping
any fraction; f32/f64 convert between each other. This is the only way to
narrow an integer — an assignment never does it on its own.
var ms: i64 = 200;
var t: i32 = _convert<i32>(ms); // 200
var f: f64 = _convert<f64>(t); // 200.0
var b: u8 = _convert<u8>(300); // 44 — the high bits are droppedUse the stdlib's safe_convert<T>(value) when the value may not fit; it returns
ConversionResult<T> with a ConversionError instead of wrapping (see Standard Library).
Enums without payloads also convert to integer types: _convert<i32>(Kind.Data)
reads the variant's value. Widening uses the signedness of the enum's
underlying integer type. An enum cannot be the target of _convert; use the
checked decoder below.
_enum_from_int<T>(value)
Decodes an integer as an enum without payloads and returns std.Option<T>.
The standard library must be loaded. A declared value returns Option.Some;
any other integer returns Option.None. The comparison happens before
narrowing, including for signed and unsigned inputs. No unsafe block is
needed. See Enums for an example.
_bitcast<T>(value)
Reinterprets the bits of a numeric value as another numeric type of the same
size — f32 ↔ u32/i32, f64 ↔ u64/i64. The sizes must match, which is
checked at compile time. Used by binary wire formats to read and write floats.
var bits: u64 = _bitcast<u64>(1.0); // 0x3FF0000000000000
var back: f64 = _bitcast<f64>(bits); // 1.0It also retypes a raw pointer — raw_ptr<A> to raw_ptr<B>, the cast C writes
as (B*)p. This is how a pointer to anything reaches a C function that takes
void*, which Sun declares as raw_ptr<u8>, and how a byte buffer is read as a
header:
extern "C" function c_send(fd: i32, buf: raw_ptr<u8>, n: i64, flags: i32) i64 as "send";
packed_class Header {
var kind: u16;
var length: u16;
init(k: u16, n: u16) { this.kind = k; this.length = n; }
}
function send_header(fd: i32, h: ref Header) i64 {
var bytes = _bitcast<raw_ptr<u8>>(_address_of<Header>(h));
unsafe { return c_send(fd, bytes, _sizeof<Header>(), 0); };
}A bitcast is numeric to numeric or pointer to pointer, never one to the other,
and never to or from a class, enum or interface. Retyping a pointer only renames
it: reading through the result still needs an unsafe block, and it is on you to
have pointed at bytes that really are a B.
Non-Generic Intrinsics
These intrinsics operate on specific, fixed types.
_malloc(size)
Allocates size bytes of heap memory and returns a raw_ptr<i8>. This is a direct wrapper around the C library's malloc. Only usable inside an unsafe block.
var mem = unsafe { _malloc(1024); }; // Allocate 1024 bytesPrefer using allocators. Direct _malloc calls bypass Sun's memory management. Use HeapAllocator.alloc_raw(size) in standard library code.
_free(ptr)
Frees memory previously allocated with _malloc. Direct wrapper around C's free. Only usable inside an unsafe block.
var mem = unsafe { _malloc(1024); };
// ... use memory ...
unsafe { _free(mem); };_memcpy(dst, src, len), _memmove(dst, src, len), and _memset(dst, value, len)
Use _memmove when the source and destination may overlap, such as when compacting a buffer. Like _memcpy, it requires an unsafe block and valid memory ranges. It performs no allocation.
_memcpy and _memmove copy len bytes from src to dst; _memset sets
len bytes at dst to a single byte value. They take raw pointers and a byte
count and lower to LLVM memory intrinsics. Only _memcpy requires the source
and destination regions not to overlap.
// Grow a buffer: move the old bytes across, then zero the new tail
unsafe {
_memcpy(newData, oldData, this.len * _sizeof<T>());
_memset(_ptr_offset(newData, this.len * _sizeof<T>()), 0, added);
};These move bytes, not values. Copying a compound value this way would leave two owners of one buffer — see Memory Safety.
_ptr_offset(ptr, byte_offset)
Advances a pointer by a number of bytes and returns the result. Unlike
_load<T>/_store<T>, which index in elements, this is plain byte arithmetic,
so an element offset is written out as index * _sizeof<T>().
var slot = unsafe { _ptr_offset(this.data, index * _sizeof<T>()); };_address_of<T>(ref T)
Returns a raw_ptr<T> pointing to the memory location of a reference. Useful for interfacing with low-level operations that require a pointer (e.g., atomics).
var x: i32 = 42;
var ptr = _address_of<i32>(x); // raw_ptr<i32> to x's memoryThe returned pointer is only valid while the referenced variable is in scope. Do not store it beyond the variable's lifetime.
_to_ref<T>(raw_ptr<T>)
The reverse of _address_of: turns a raw_ptr<T> into a ref T so the
pointed-to value can be read, borrowed or assigned through like any reference.
Nothing checks that the pointer is non-null or still points at a live T, so
write it inside unsafe { } — the standard library does so at every use. The
borrow checker gives the reference the lifetime of the pointer, so the pointer
must stay valid for as long as the reference is used.
var x: i32 = 1;
var p: raw_ptr<i32> = _address_of<i32>(x);
var r: ref i32 = unsafe { _to_ref<i32>(p); };
r = 2; // x is now 2_load_i64(ptr, index)
Loads an i64 from ptr at element offset index. A non-generic version of _load<i64>. Only usable inside an unsafe block.
var value = unsafe { _load_i64(data, 0); }; // Load first i64_store_i64(ptr, index, value)
Stores an i64 value at ptr[index]. A non-generic version of _store<i64>. Only usable inside an unsafe block.
unsafe { _store_i64(data, 0, 42); }; // Store 42 at first positionPrint Intrinsics
These intrinsics provide low-level output capabilities. They are used internally by the standard library's print.sun module.
_print_i32(value)
Prints an i32 value to stdout (no newline).
_print_i64(value)
Prints an i64 value to stdout (no newline).
_print_u64(value)
Prints a u64 value to stdout (no newline). Unlike _print_i64, a value above
the i64 maximum prints as the full unsigned number.
_print_f64(value)
Prints an f64 value to stdout (no newline).
_print_newline()
Prints a newline character to stdout.
_print_char(c)
Prints a single char to stdout, encoded as UTF-8. A char holds a Unicode
scalar value, so one call may write between one and four bytes (see
Characters and Bytes).
_print_char('é'); // writes two bytes_print_bytes(ptr, len)
Prints len bytes from the memory at ptr to stdout.
var s: static_ptr<u8> = "hello";
_print_bytes(s.raw(), s.length()); // Prints "hello"The pointer and length of a static_ptr<T> come from its raw() and
length() methods, not from an intrinsic (see
Builtin Types).
_println_str(str)
Prints a static_ptr<u8> string followed by a newline.
_println_str("Hello, world!");Atomic Intrinsics
Atomic intrinsics provide low-level integer synchronization. They are unsafe
because the compiler cannot prove that a raw pointer stays live, is correctly
aligned, or is used atomically everywhere. Each pointer must address a value
whose type matches the intrinsic suffix.
The supported suffixes are i32, i64, and u64. For example,
_atomic_load_i64 accepts a raw_ptr<i64> and returns an i64.
Memory ordering
The ordering is part of each intrinsic rather than a run-time parameter:
| Operation | Ordering |
|---|---|
_atomic_load_* | Acquire |
_atomic_store_* | Release |
_atomic_cmpxchg_* | Acquire-release on success, acquire on failure |
_atomic_fetch_add_*, _atomic_fetch_sub_* | Acquire-release |
_atomic_fence_acquire | Acquire fence |
_atomic_fence_release | Release fence |
An acquire operation that observes a value from a release operation on the same atomic location also observes the ordinary writes that happened before that release. This is enough for common publication and single-producer, single-consumer queue patterns. These intrinsics do not provide relaxed or sequentially consistent ordering.
_atomic_cmpxchg_i32/i64/u64(ptr, expected, desired)
Atomically compares the value at ptr with expected. If they are equal, it
stores desired. The return value is always the value that was at ptr, so the
exchange succeeded when the return value equals expected.
var state: i32 = 0;
var old = unsafe {
_atomic_cmpxchg_i32(_address_of<i32>(state), 0, 1);
};_atomic_store_i32/i64/u64(ptr, value)
Stores value with release ordering.
var sequence: u64 = 0;
var next: u64 = 42;
unsafe { _atomic_store_u64(_address_of<u64>(sequence), next); };_atomic_load_i32/i64/u64(ptr)
Loads the value with acquire ordering.
var current = unsafe {
_atomic_load_u64(_address_of<u64>(sequence));
};_atomic_fetch_add_i32/i64/u64 / _atomic_fetch_sub_i32/i64/u64
Atomically adds or subtracts delta with acquire-release ordering and returns
the value from before the operation. That is what a reference count needs:
whoever reads 1 from a decrement was the last owner, so exactly one caller runs
the cleanup.
var delta: u64 = 1;
var before = unsafe {
_atomic_fetch_add_u64(_address_of<u64>(sequence), delta);
};_atomic_fence_acquire() / _atomic_fence_release()
Emit explicit acquire and release fences. A release fence keeps earlier memory operations before the fence; an acquire fence keeps later memory operations after it. Use the acquire/release loads and stores directly when one atomic location carries the synchronization signal.
unsafe { _atomic_fence_release(); };
unsafe { _atomic_fence_acquire(); };The compiler emits LLVM atomic operations. Whether a width is implemented with a lock-free instruction is a property of the compilation target.
Bit Intrinsics
Wide multiplication and zero counts, the building blocks of multi-limb integer
arithmetic (BigUint) and table-driven float conversion.
_mul_hi_u64(a, b)
The high 64 bits of the 128-bit product a * b (the low 64 bits are just a * b).
var lo: u64 = a * b;
var hi: u64 = _mul_hi_u64(a, b);_ctlz_u64(x) / _cttz_u64(x)
Number of leading / trailing zero bits in a u64; both return 64 for 0.
var bits: u64 = 64 - _ctlz_u64(x); // bit length of x_bswap_u16(x) / _bswap_u32(x) / _bswap_u64(x)
The value with its bytes in the opposite order. Each lowers to a single instruction on every target Sun supports.
var swapped: u32 = _bswap_u32(305419896); // 0x12345678 -> 0x78563412Most code should reach for
std.byte_order instead, whose helpers say which order
the bytes are going to or coming from.
Target Intrinsics
_target_is(name)
A compile-time check of the operating system or architecture being compiled for.
The argument must be one of "linux", "macos", "windows", "x86_64", or
"aarch64" (anything else is a compile error). The call folds to a constant
for the --target in effect, or the host when compiling natively.
"aarch64" also matches targets spelled arm64-apple-darwin.
A branch on a folded condition keeps only its live side — dead branches are pruned during code generation, not left to an optimizer — so per-OS code may call C symbols the other systems' libraries lack:
var CLOCK_MONOTONIC: i32 = _target_is("macos") ? 6 : 1;
if (_target_is("macos")) {
// only ever compiled into macOS binaries
}For whole files of per-OS definitions, prefer the manifest's target: block
(see Modules). Manifest selectors accept operating systems;
architecture checks are available through _target_is.
Futex Intrinsics
Wait-on-address primitives, used by Mutex for efficient blocking. On Linux
targets they lower to the futex syscall; on macOS targets, to the
equivalent __ulock_wait/__ulock_wake.
_futex_wait(ptr, expected)
Blocks the calling thread if the value at ptr equals expected. The thread sleeps until woken by _futex_wake.
unsafe { _futex_wait(_address_of<i32>(state), 2); }; // Sleep if state == 2_futex_wake(ptr)
Wakes one thread blocked on _futex_wait at the given address.
unsafe { _futex_wake(_address_of<i32>(state)); }; // Wake one waiterAtomic and futex intrinsics are low-level primitives for implementing synchronization. Use Mutex from the standard library instead.
File Intrinsics
Thin wrappers over the C library's file calls, spelled with two leading
underscores. They exist so std.io can be written in Sun without an extern
declaration per platform; user code should use std.io instead.
Every one of these needs an unsafe block. They keep libc's conventions: file
descriptors are i32, and a failure comes back as a negative value rather than
a thrown error.
| Intrinsic | Returns | Description |
|---|---|---|
__file_open(path: static_ptr<u8>, flags: i32) | i32 | Open path, returning a file descriptor |
__file_close(fd: i32) | i32 | Close a descriptor |
__file_write(fd: i32, data: static_ptr<u8>) | i32 | Write a string to fd |
__file_read(fd: i32, count: i32) | raw_ptr<i8> | Read count bytes into a freshly allocated buffer |
__write(fd: i32, buf: raw_ptr<u8>, len: i64) | i64 | Write len bytes from buf |
__read(fd: i32, buf: raw_ptr<u8>, len: i64) | i64 | Read up to len bytes into buf |
__lseek(fd: i32, offset: i64, whence: i32) | i64 | Move the file position; whence is 0 (set), 1 (current) or 2 (end) |
__fstat(fd: i32, stat_buf: raw_ptr<i8>) | i32 | Fill a libc struct stat at stat_buf |
__fsync(fd: i32) | i32 | Flush written data to the device |
__ftruncate(fd: i32, length: i64) | i32 | Truncate or extend the file to length |
__unlink(path: static_ptr<u8>) | i32 | Remove a file |
__rename(old_path, new_path: static_ptr<u8>) | i32 | Rename or move a file |
__mkdir(path: static_ptr<u8>, mode: i32) | i32 | Create a directory with the given permission bits |
__rmdir(path: static_ptr<u8>) | i32 | Remove an empty directory |
// Read a whole descriptor into a caller-owned buffer
var got: i64 = unsafe { __read(fd, buffer, capacity); };
if (got < 0) { /* -errno */ }__file_read allocates the buffer it returns, so the caller frees it with
_free. __read writes into a buffer you already own, which is what
std.io uses.
Socket Intrinsics
The same arrangement for the C library's socket calls, used by std.net. An
address is a pointer to a libc sockaddr — Sun has no struct for it, so it is
passed as raw_ptr<u8> with its length. A failure is a negative errno.
| Intrinsic | Returns | Description |
|---|---|---|
__socket(domain: i32, type: i32, protocol: i32) | i32 | Create a socket. domain is 1 (Unix), 2 (IPv4) or 10 (IPv6); type is 1 (stream) or 2 (datagram); protocol is usually 0 |
__bind(fd: i32, addr: raw_ptr<u8>, addrlen: i32) | i32 | Bind to a local address |
__listen(fd: i32, backlog: i32) | i32 | Start listening, with room for backlog pending connections |
__accept(fd: i32, addr: raw_ptr<u8>, addrlen: i32) | i32 | Accept a connection, returning a new descriptor |
__connect(fd: i32, addr: raw_ptr<u8>, addrlen: i32) | i32 | Connect to a remote address |
__send(fd: i32, buf: raw_ptr<u8>, len: i64, flags: i32) | i64 | Send bytes, returning the count sent |
__recv(fd: i32, buf: raw_ptr<u8>, len: i64, flags: i32) | i64 | Receive bytes, returning the count received |
__shutdown(fd: i32, how: i32) | i32 | Half-close: 0 (read), 1 (write) or 2 (both) |
__setsockopt(fd, level, optname: i32, optval: raw_ptr<u8>, optlen: i32) | i32 | Set a socket option — SOL_SOCKET is level 1, SO_REUSEADDR option 2 |
__getsockopt(fd, level, optname: i32, optval: raw_ptr<u8>, optlen: raw_ptr<i32>) | i32 | Read a socket option; optlen is read and written |
IPv4 shorthands
Building a sockaddr_in by hand means knowing a layout that differs between
Linux and macOS, and getting the port into network byte order. These intrinsics
do that part, taking the address and port as plain integers:
| Intrinsic | Returns | Description |
|---|---|---|
__bind_ipv4(fd: i32, ip: i32, port: i32) | i32 | Bind to an IPv4 address |
__connect_ipv4(fd: i32, ip: i32, port: i32) | i32 | Connect to an IPv4 address |
__accept_fd(fd: i32) | i32 | Accept, discarding the peer's address |
__sendto_ipv4(fd: i32, buf: raw_ptr<u8>, len: i64, flags: i32, ip: i32, port: i32) | i64 | Send one datagram to an IPv4 address, returning the count sent |
__recvfrom_ipv4(fd: i32, buf: raw_ptr<u8>, len: i64, flags: i32, out_ip: raw_ptr<i32>, out_port: raw_ptr<i32>) | i64 | Receive one datagram, writing the sender's address and port through the out-pointers |
__getsockname_ipv4(fd: i32, out_ip: raw_ptr<i32>, out_port: raw_ptr<i32>) | i32 | Report the socket's own bound address and port through the out-pointers |
The compiler lays out the sockaddr_in for the target operating system and
byte-swaps ports into network order. An ip is always in network order — 0 for
"any address", which is what a server binds to — while ports cross the boundary
in host order, both directions:
var fd: i32 = unsafe { __socket(2, 1, 0); }; // AF_INET, SOCK_STREAM
var rc: i32 = unsafe { __bind_ipv4(fd, 0, 8080); }; // 0.0.0.0:8080
unsafe { __listen(fd, 16); };
var client: i32 = unsafe { __accept_fd(fd); };Usage in Standard Library
The standard library uses intrinsics to implement generic containers. Here's a simplified example of how Vec<T> might use them:
class Vec<T> {
data: raw_ptr<i8>;
len: i64;
cap: i64;
alloc: HeapAllocator;
method get(index: i64) T {
return unsafe { _load<T>(this.data, index); };
}
method set(index: i64, value: T) void {
unsafe { _store<T>(this.data, index, value); };
}
method push(value: T) void {
if (this.len >= this.cap) {
this.grow();
}
unsafe { _store<T>(this.data, this.len, value); };
this.len = this.len + 1;
}
method grow() void {
var newCap = this.cap * 2;
var newData = this.alloc.alloc_raw(newCap * _sizeof<T>());
unsafe {
_memcpy(newData, this.data, this.len * _sizeof<T>());
_free(this.data);
};
this.data = newData;
this.cap = newCap;
}
}Summary Table
| Intrinsic | Parameters | Returns | Description |
|---|---|---|---|
_sizeof<T> | none | i64 | Byte size of type T |
_init<T> | ptr, args... | void | Construct T at ptr |
_deinit<T> | ptr | void | Release the T at ptr without freeing the storage |
_params_of<T> | none | — | The parameter list an args... pack stands for |
_load<T> | ptr, index | T | Load element at index |
_store<T> | ptr, index, value | void | Store element at index |
_ptr_as_raw<T> | ptr<T> | raw_ptr<T> | Get raw pointer without ownership transfer |
_is<T> | value | bool | Compile-time type check |
_convert<T> | value | T | Unchecked numeric conversion (truncate, extend, int ↔ float) |
_bitcast<T> | value | T | Reinterpret bits as a same-size numeric type, or retype a raw_ptr |
_address_of<T> | ref T | raw_ptr<T> | Get pointer to referenced memory |
_to_ref<T> | raw_ptr<T> | ref T | Reference the pointed-to value (unsafe) |
_malloc | size: i64 | raw_ptr<i8> | Allocate heap memory |
_free | ptr | void | Free heap memory |
_memcpy | dst, src, len: i64 | void | Copy len bytes between non-overlapping regions |
_memmove | dst, src, len: i64 | void | Copy len bytes, allowing overlapping regions |
_memset | dst, value, len: i64 | void | Set len bytes at dst to one byte value |
_ptr_offset | ptr, byte_offset: i64 | raw_ptr | Advance a pointer by a byte count |
_load_i64 | ptr, index | i64 | Load i64 at index |
_store_i64 | ptr, index, value | void | Store i64 at index |
_print_i32 | value: i32 | void | Print i32 to stdout |
_print_i64 | value: i64 | void | Print i64 to stdout |
_print_u64 | value: u64 | void | Print u64 to stdout |
_print_f64 | value: f64 | void | Print f64 to stdout |
_print_char | c: char | void | Print a char as UTF-8 |
_print_newline | none | void | Print newline |
_print_bytes | ptr, len | void | Print bytes to stdout |
_println_str | static_ptr<u8> | void | Print string with newline |
_atomic_cmpxchg_i32/i64/u64 | ptr, expected, desired | matching integer | Atomic compare-and-exchange |
_atomic_store_i32/i64/u64 | ptr, value | void | Release store |
_atomic_load_i32/i64/u64 | ptr | matching integer | Acquire load |
_atomic_fetch_add_i32/i64/u64 | ptr, delta | matching integer | Atomic add, returns the old value |
_atomic_fetch_sub_i32/i64/u64 | ptr, delta | matching integer | Atomic subtract, returns the old value |
_atomic_fence_acquire | none | void | Acquire fence |
_atomic_fence_release | none | void | Release fence |
_mul_hi_u64 | a: u64, b: u64 | u64 | High 64 bits of the 128-bit product |
_ctlz_u64 | x: u64 | u64 | Leading zero bits (64 for 0) |
_cttz_u64 | x: u64 | u64 | Trailing zero bits (64 for 0) |
_bswap_u16 | x: u16 | u16 | The value with its bytes reversed |
_bswap_u32 | x: u32 | u32 | The value with its bytes reversed |
_bswap_u64 | x: u64 | u64 | The value with its bytes reversed |
_futex_wait | ptr, expected | void | Block until woken |
_futex_wake | ptr | void | Wake one blocked thread |
_target_is | name: string literal | bool | Compile-time check of the target OS or architecture |
The __-prefixed file and socket intrinsics have tables of their own above.