Compiler Intrinsics

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 Point

For 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 lifetime
⚠️

Be 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:

TraitMatches
_Integeri8, i16, i32, i64, u8, u16, u32, u64
_Signedi8, i16, i32, i64
_Unsignedu8, u16, u32, u64
_Floatf32, f64
_NumericAll integers and floats
_PrimitiveAll numeric types plus bool
_LambdaAny closure type
_FunctionA 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 dropped

Use 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.0

It 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 bytes
🚫

Prefer 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 memory
⚠️

The 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 position

Print 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:

OperationOrdering
_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_acquireAcquire fence
_atomic_fence_releaseRelease 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 -> 0x78563412

Most 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 waiter
⚠️

Atomic 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.

IntrinsicReturnsDescription
__file_open(path: static_ptr<u8>, flags: i32)i32Open path, returning a file descriptor
__file_close(fd: i32)i32Close a descriptor
__file_write(fd: i32, data: static_ptr<u8>)i32Write 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)i64Write len bytes from buf
__read(fd: i32, buf: raw_ptr<u8>, len: i64)i64Read up to len bytes into buf
__lseek(fd: i32, offset: i64, whence: i32)i64Move the file position; whence is 0 (set), 1 (current) or 2 (end)
__fstat(fd: i32, stat_buf: raw_ptr<i8>)i32Fill a libc struct stat at stat_buf
__fsync(fd: i32)i32Flush written data to the device
__ftruncate(fd: i32, length: i64)i32Truncate or extend the file to length
__unlink(path: static_ptr<u8>)i32Remove a file
__rename(old_path, new_path: static_ptr<u8>)i32Rename or move a file
__mkdir(path: static_ptr<u8>, mode: i32)i32Create a directory with the given permission bits
__rmdir(path: static_ptr<u8>)i32Remove 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.

IntrinsicReturnsDescription
__socket(domain: i32, type: i32, protocol: i32)i32Create 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)i32Bind to a local address
__listen(fd: i32, backlog: i32)i32Start listening, with room for backlog pending connections
__accept(fd: i32, addr: raw_ptr<u8>, addrlen: i32)i32Accept a connection, returning a new descriptor
__connect(fd: i32, addr: raw_ptr<u8>, addrlen: i32)i32Connect to a remote address
__send(fd: i32, buf: raw_ptr<u8>, len: i64, flags: i32)i64Send bytes, returning the count sent
__recv(fd: i32, buf: raw_ptr<u8>, len: i64, flags: i32)i64Receive bytes, returning the count received
__shutdown(fd: i32, how: i32)i32Half-close: 0 (read), 1 (write) or 2 (both)
__setsockopt(fd, level, optname: i32, optval: raw_ptr<u8>, optlen: i32)i32Set 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>)i32Read 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:

IntrinsicReturnsDescription
__bind_ipv4(fd: i32, ip: i32, port: i32)i32Bind to an IPv4 address
__connect_ipv4(fd: i32, ip: i32, port: i32)i32Connect to an IPv4 address
__accept_fd(fd: i32)i32Accept, discarding the peer's address
__sendto_ipv4(fd: i32, buf: raw_ptr<u8>, len: i64, flags: i32, ip: i32, port: i32)i64Send 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>)i64Receive 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>)i32Report 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

IntrinsicParametersReturnsDescription
_sizeof<T>nonei64Byte size of type T
_init<T>ptr, args...voidConstruct T at ptr
_deinit<T>ptrvoidRelease the T at ptr without freeing the storage
_params_of<T>none—The parameter list an args... pack stands for
_load<T>ptr, indexTLoad element at index
_store<T>ptr, index, valuevoidStore element at index
_ptr_as_raw<T>ptr<T>raw_ptr<T>Get raw pointer without ownership transfer
_is<T>valueboolCompile-time type check
_convert<T>valueTUnchecked numeric conversion (truncate, extend, int ↔ float)
_bitcast<T>valueTReinterpret bits as a same-size numeric type, or retype a raw_ptr
_address_of<T>ref Traw_ptr<T>Get pointer to referenced memory
_to_ref<T>raw_ptr<T>ref TReference the pointed-to value (unsafe)
_mallocsize: i64raw_ptr<i8>Allocate heap memory
_freeptrvoidFree heap memory
_memcpydst, src, len: i64voidCopy len bytes between non-overlapping regions
_memmovedst, src, len: i64voidCopy len bytes, allowing overlapping regions
_memsetdst, value, len: i64voidSet len bytes at dst to one byte value
_ptr_offsetptr, byte_offset: i64raw_ptrAdvance a pointer by a byte count
_load_i64ptr, indexi64Load i64 at index
_store_i64ptr, index, valuevoidStore i64 at index
_print_i32value: i32voidPrint i32 to stdout
_print_i64value: i64voidPrint i64 to stdout
_print_u64value: u64voidPrint u64 to stdout
_print_f64value: f64voidPrint f64 to stdout
_print_charc: charvoidPrint a char as UTF-8
_print_newlinenonevoidPrint newline
_print_bytesptr, lenvoidPrint bytes to stdout
_println_strstatic_ptr<u8>voidPrint string with newline
_atomic_cmpxchg_i32/i64/u64ptr, expected, desiredmatching integerAtomic compare-and-exchange
_atomic_store_i32/i64/u64ptr, valuevoidRelease store
_atomic_load_i32/i64/u64ptrmatching integerAcquire load
_atomic_fetch_add_i32/i64/u64ptr, deltamatching integerAtomic add, returns the old value
_atomic_fetch_sub_i32/i64/u64ptr, deltamatching integerAtomic subtract, returns the old value
_atomic_fence_acquirenonevoidAcquire fence
_atomic_fence_releasenonevoidRelease fence
_mul_hi_u64a: u64, b: u64u64High 64 bits of the 128-bit product
_ctlz_u64x: u64u64Leading zero bits (64 for 0)
_cttz_u64x: u64u64Trailing zero bits (64 for 0)
_bswap_u16x: u16u16The value with its bytes reversed
_bswap_u32x: u32u32The value with its bytes reversed
_bswap_u64x: u64u64The value with its bytes reversed
_futex_waitptr, expectedvoidBlock until woken
_futex_wakeptrvoidWake one blocked thread
_target_isname: string literalboolCompile-time check of the target OS or architecture

The __-prefixed file and socket intrinsics have tables of their own above.