Graph-free LEAF with this array's exact bytes — ONE copy, straight from
the (evaluated) mlx buffer into a fresh mlx-owned array, no JS-heap
intermediate (fromBytesCopy(rawBytes()) is TWO copies: device→JS heap
→new leaf). mlx_array_new_data copies synchronously, so the source only
has to outlive this call. The buffer is read LINEARLY — the caller must
pass a row-major contiguous array (ops.contiguous first for views), same
contract as rawBytes. Segmented-backward boundary detach hot path.
Raw bytes of a supported floating-point or integer array (copy).
ZERO-COPY view of the evaluated array's bytes — aliases the mlx buffer directly, no JS-heap copy. Valid only while THIS array is alive and unmutated: do not retain past dispose(). Built for bulk-write paths (packages/inference/src/state/persistence.ts's streamed writer) where per-tensor rawBytes() copies pile up in the JS heap faster than GC collects them — the residual A7 whole-entry RSS spike (2026-07-07). The array must be contiguous (ops.contiguous first for views), same contract as rawBytes.
Basic slice: start/stop per dimension (stride 1).
Publish an evaluated immutable buffer to a CPU reader. Strides are in elements, so padding, transposes and slices need no GPU packing copy. The caller retains this array until the reader reports completion.
Read back as float32 (casts on GPU if needed, then copies out).
Read an f32/f16/bf16 array numerically as float32 without enqueueing a GPU cast. This evaluates the ORIGINAL array, then expands 16-bit values on the host. Use this for small readbacks after later GPU work has already been dispatched: toFloat32() would append astype(float32) behind that work and turn the readback into an accidental full-step barrier.
Read back an INTEGER array without enqueueing a cast kernel: eval +
direct buffer read for uint32/int32. A cast (astype) is a NEW op that
queues BEHIND everything already dispatched on the GPU stream — on the
batched decode hot path, reading the pipeline register via toFloat32
stalled the "overlapped" token read for a FULL step of GPU work every
token (the core of the B=1 decode gap).
Non-integer dtypes fall back to the rounded float readback.
Release the GC root fromView placed on this array's host buffer. JS-thread-only by construction (it's a plain method) — this is the replacement for the deadlocking JSCallback unpin dtor. Idempotent; no-op for non-fromView arrays. Caller contract: only call once mlx can no longer touch the buffer (see fromView doc), or when the memory is owned elsewhere (an mmap kept mapped past every use).
StaticadoptAdopt a page-aligned CPU restore allocation without a host copy. The native destructor owns it after this call, including GPU references.
StaticdequantizeStaticfromCopy raw bytes into a fresh mlx-managed (page-aligned) LEAF array of the
given dtype/shape. Unlike fromView (which no-copy-wraps the host buffer and
is unsafe to feed to GPU ops when unaligned), mlx_array_new_data COPIES, so
the source bytes need not outlive the array and GPU ops are safe. Used to
detach a value from its computation graph: MlxArray.fromBytesCopy( x.rawBytes(), x.shape, x.dtype) yields a graph-free leaf with x's exact
bytes (segmented-backward boundaries — packages/training/src/segmented.ts).
StaticfromCopying constructor for small host data.
StaticfromStaticfromZero-copy: wrap a raw pointer (e.g. into an mmap'd weight shard). The caller guarantees the memory outlives EVERY mlx reference to the array — including GPU command buffers that retain the buffer past dispose() — i.e. mmaps backing these arrays stay mapped for the process. The dtor is free(NULL) (payload 0): native + no-op, so mlx may run it from the Metal completion thread without touching the JS runtime (the old JSCallback dtor deadlocked serving / SIGTRAPed under GC when that thread held the last reference — 2026-07-06).
StaticfromZero-copy: wrap an existing JS buffer. The view is pinned (GC-rooted) in a process-side map; mlx gets the NATIVE no-op dtor (free(NULL)) — never a JS callback, so the last release may happen on the Metal completion thread without deadlock (see hazard note above). Buffer lifetime is therefore the CALLER's contract:
Cast to another dtype (lazy).