SIMD Vectors

Fixed-size numeric lane values, vector operators, and bounds rules.

vector<T, N> is a fixed-size numeric lane value. It is not an array or slice. The public contract is the lane semantics documented here; backend lowering, register selection, and instruction choice are implementation details.

fn main() {
    let a: vector<i32, 4> = [1, 2, 3, 4];
    let b: vector<i32, 4> = [10, 20, 30, 40];
    let c = a + b;
    let first = c[0];
    let last = c[3];

    assert first == 11;
    assert last == 44;
    assert a < b;
}

Type Rules

The element type must be an integer scalar or floating-point scalar. bool, references, pointers, arrays, nullable types, error unions, and unique values are not valid vector element types.

let ints: vector<i32, 4> = [1, 2, 3, 4];
let bytes: vector<u8, 16> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
let floats: vector<f32, 4> = [1.0, 2.0, 3.0, 4.0];

The lane count N must be a positive integer literal.

Literals

Vector values use array-literal syntax when the expected vector type is known. The literal length must exactly match the lane count.

fn first(v: vector<i32, 4>) -> i32 {
    return v[0];
}

fn values() -> vector<i32, 4> {
    return [2, 4, 6, 8];
}

struct Sample {
    lanes: vector<i32, 4>,
}

fn main() {
    let typed: vector<i32, 4> = [1, 2, 3, 4];
    let one = first([5, 6, 7, 8]);
    let returned = values();
    let sample = Sample { lanes: [3, 6, 9, 12] };
    let typed_first = typed[0];
    let returned_last = returned[3];
    let sample_lane = sample.lanes[2];

    assert typed_first == 1;
    assert one == 5;
    assert returned_last == 8;
    assert sample_lane == 9;
}

An untyped vector literal does not stand alone. Give it a vector type through a typed binding, function parameter, return type, or struct field.

Operators

Vector operators work only between matching vector types. The element type and lane count must be identical on both sides.

OperationOperatorsResult
Arithmetic+, -, *, /, %same vector type
Integer bitwise&, `, ^`
Integer shift<<, >>same vector type
Comparison==, !=, <, <=, >, >=scalar bool

Integer-vector arithmetic follows scalar numeric policy lane-by-lane: +, -, and * trap if any lane overflows; / and % trap if any lane divides by zero or hits signed min / -1 or min % -1. Floating-vector arithmetic follows scalar floating-point policy per lane.

Vector shifts discard shifted-out bits, but each lane's shift count must be less than the element bit width. If any lane has an oversized or negative shift count, the program traps.

Comparisons are true only when every lane satisfies the comparison. They do not produce mask vectors.

fn main() {
    let a: vector<u32, 4> = [1, 3, 7, 15];
    let b: vector<u32, 4> = [8, 12, 14, 15];
    let masked = (a & b) ^ b;

    let shift: vector<u32, 4> = [1, 1, 1, 1];
    let shifted = a << shift;
    let masked0 = masked[0];
    let masked3 = masked[3];
    let shifted0 = shifted[0];
    let shifted3 = shifted[3];

    assert masked0 == 8 as u32;
    assert masked3 == 0 as u32;
    assert shifted0 == 2 as u32;
    assert shifted3 == 30 as u32;
}

There is no scalar splatting or mixed scalar/vector arithmetic.

let a: vector<i32, 4> = [1, 2, 3, 4];
let bad = a + 1; // error: vector arithmetic requires matching vector operands

Indexing

Use v[i] to read a lane. The same syntax can be the target for direct element assignment or compound assignment.

import std.os;

fn main() {
    let v: vector<i32, 4> = [1, 2, 3, 4];
    let i: usize = 2;

    v[1] = 9;
    v[i] += 5;

    _ = os.write("{v[0]} {v[1]} {v[2]} {v[3]}\n");
}

Constant out-of-bounds indexes are compile-time errors.

let v: vector<i32, 4> = [1, 2, 3, 4];
let bad = v[4]; // error: vector index 4 out of bounds (size 4)

Dynamic indexes are checked at runtime. If the index is out of bounds, the program traps with a vector bounds message.

Current Limitations

  • Vector operations are vector-vector only.
  • Scalar splats such as v + 1 are not supported.
  • Vector literals need an expected vector type.
  • Comparisons reduce to one scalar bool, not a mask vector.
  • No target-specific SIMD intrinsic surface is part of 0.0.0-dev.