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;
}
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.
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.
Vector operators work only between matching vector types. The element type and lane count must be identical on both sides.
| Operation | Operators | Result |
|---|---|---|
| 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
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.
- Vector operations are vector-vector only.
- Scalar splats such as
v + 1are 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.