Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

A small portable std.simd surface

This records the proposed explicit API for portable SIMD. The initial module implements a subset using existing copyable enums, module factories and named operations; its implementation assessment explains differences from this sketch. Packed views, static type factories, operator syntax and fully opaque logical masks below remain proposed.

Values users can understand

Start with concrete types such as simd.f32x4, simd.f64x2, simd.i32x4 and simd.u32x4. Each is an inline fixed-size value with ordinary copy semantics, not a heap object or a promise of one hardware register. Qualify additional integer widths only when their operations and scalar fallback are tested. Keep a closed initial set rather than making users learn const generics or backend capability traits. Named aliases can survive a later generic design.

Rust’s Simd<T, N> is a useful model: lane operations, splats, comparisons, selection and portable load/store behavior. Its full API is not the starting scope. In particular, do not expose raw pointer vectors, unsafe loads, arbitrary gather/scatter, dynamic swizzles or architecture-specific intrinsics by default.

SurfaceProposed contract
splat(value)Repeat one scalar into all lanes; allocation free
Fixed-arity new(...), lane(index)Construct without allocating a list; lane index is checked
Floating +, -, *, /Scalar lane semantics, without implicit fusion/reassociation
Integer +, -, *Checked like ordinary Plenty; first implementation may scalarize checks
Integer wrapping_add/sub/mulExplicit modular arithmetic, matching existing scalar methods
eq, lt, le, etc.Produce an opaque lane mask, not one boolean
mask.select(yes, no)Select already-computed lanes; this is not lazy conditional execution
mask.any(), mask.all(), mask.count()Boolean/count summaries; no exposed integer encoding
load(view, offset), store(view, offset)Check a complete vector fits; accept scalar-aligned memory; no overread
wrapping_sum() on integer vectorsExplicit modular reduction, independent of grouping
sum_ordered(initial) on float vectorsScalar left fold in increasing lane order; no speedup promised

Each comparison mask is compatible only with vectors of the corresponding lane count/shape; there are no implicit integer-mask casts. Avoid operator-overloading ambiguity around equality returning a mask: explicit comparison methods suffice. Mask bit representation, alignment and calling convention are private.

Keep Plenty’s arithmetic contract

Rust SIMD integer operators wrap. Plenty’s ordinary integer operators trap on overflow, so copying Rust’s operator behavior would make changing a scalar into a vector silently change the algorithm. Keep checked operators, expose wrapping by its existing name, and allow checked vectors to use scalar fallback initially. For an explicit checked operation, report the lowest failing lane and operation site. Do not publish partially written stores after a failed bounds check.

No initial integer division, shifts with new count rules, saturating casts, approximate reciprocal, fused multiply-add, or min/max with underspecified NaN behavior. Add each only with a complete scalar contract and target tests. Explicit fused or relaxed operations can be considered later; their names must make the semantic difference visible.

Floating lanes follow Plenty’s scalar rules for signed zero, infinities, NaNs and subnormals on a supported target. Do not promise bit-identical NaN payloads across platforms beyond the scalar contract. Rust documents subnormal flushing on some older architectures; Plenty must either provide an appropriate fallback or document the target’s scalar contract before enabling SIMD there. Do not silently weaken scalar semantics to accommodate an instruction.

Bounds, tails and ownership belong below the algorithm

Safe load/store accept the eventual checked contiguous numeric view, retaining its owner and borrow lifetime. No caller-supplied alignment proof is needed. Loads do not allocate; stores require an exclusive mutable view and validate all destination bounds before writing. The initial C ABI uses pointers/lengths and scalar elements, never a vector passed by value.

For manual kernels, provide a library block-view helper once packed views exist: conceptually simd.blocks4(view) splits a borrow into full four-lane blocks and a scalar tail. Each block has a safe load()/store() pair. The helper checks the split once, does not allocate or create overlapping mutable loans, and requires the tail to be handled explicitly. This is an ergonomic target, not an implemented borrowing API. Do not require every user to write alignment prefixes or masks for ordinary remainders.

Prefer full blocks plus scalar tail over padded masked evaluation initially. Padding memory safely is insufficient if inactive lanes divide by zero or trap on a cast. A future masked-load API needs an explicit inactive value and memory contract; selection alone does not suppress evaluation of either operand.

For everyday algorithms, automatic pipeline lowering performs the split and tail handling invisibly. Users reach for explicit vectors only when lane relationships are part of the algorithm. If common examples require repeated mask and tail bookkeeping, improve the safe helper or the automatic pattern before broadening the intrinsic catalog.

Target policy and explanations

Use a documented deployment baseline by default. A native-target build may require newer CPU features, but that is a build contract, not a different source API. Runtime dispatch, feature probing and multiple compiled variants remain separate extensions with code-size and startup costs.

Lack of a SIMD instruction must never make a valid portable program fail to compile: split or scalarize. A vector IR type’s existence is not a support check. Query a tested operation/type capability table before lowering and exercise its fallback path in tests. A backend compilation error should be a compiler defect, not the user’s mechanism for selecting vector widths.