diff --git a/doc/langref.html.in b/doc/langref.html.in index d8887cd51a..b48db769cc 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2483,23 +2483,86 @@ test "null terminated array" { {#header_open|Vectors#}

A vector is a group of booleans, {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on - in parallel using a single instruction ({#link|SIMD#}). Vector types are created with the builtin function {#link|@Type#}, - or using the shorthand as {#syntax#}std.meta.Vector{#endsyntax#}. + in parallel using SIMD instructions. Vector types are created with the builtin function {#link|@Type#}, + or using the shorthand function {#syntax#}std.meta.Vector{#endsyntax#}.

- TODO talk about C ABI interop + Vectors support the same builtin operators as their underlying base types. These operations are performed + element-wise, and return a vector of the same length as the input vectors. This includes: +

- {#header_open|SIMD#}

- TODO Zig's SIMD abilities are just beginning to be fleshed out. Here are some talking points to update the - docs with: - * What kind of operations can you do? All the operations on integers and floats? What about mixing scalar and vector? - * How to convert to/from vectors/arrays - * How to access individual elements from vectors, how to loop over the elements - * "shuffle" - * Advice on writing high perf software, how to abstract the best way + It is prohibited to use a math operator on a mixture of scalars (individual numbers) and vectors. + Zig provides the {#link|@splat#} builtin to easily convert from scalars to vectors, and it supports {#link|@reduce#} + and array indexing syntax to convert from vectors to scalars. Vectors also support assignment to and from + fixed-length arrays with comptime known length.

- {#header_close#} +

+ For rearranging elements within and between vectors, Zig provides the {#link|@shuffle#} and {#link|@select#} functions. +

+

+ Operations on vectors shorter than the target machine's native SIMD size will typically compile to single SIMD + instructions, while vectors longer than the target machine's native SIMD size will compile to multiple SIMD + instructions. If a given operation doesn't have SIMD support on the target architecture, the compiler will default + to operating on each vector element one at a time. Zig supports any comptime-known vector length up to 2^32-1, + although small powers of two (2-64) are most typical. Note that excessively long vector lengths (e.g. 2^20) may + result in compiler crashes on current versions of Zig. +

+ {#code_begin|test|vector_example#} +const std = @import("std"); +const Vector = std.meta.Vector; +const expectEqual = std.testing.expectEqual; + +test "Basic vector usage" { + // Vectors have a compile-time known length and base type, + // and can be assigned to using array literal syntax + const a: Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; + const b: Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; + + // Math operations take place element-wise + const c = a + b; + + // Individual vector elements can be accessed using array indexing syntax. + try expectEqual(6, c[0]); + try expectEqual(8, c[1]); + try expectEqual(10, c[2]); + try expectEqual(12, c[3]); +} + +test "Conversion between vectors, arrays, and slices" { + // Vectors and fixed-length arrays can be automatically assigned back and forth + var arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 }; + var vec: Vector(4, f32) = arr1; + var arr2: [4]f32 = vec; + try expectEqual(arr1, arr2); + + // You can also assign from a slice with comptime-known length to a vector using .* + const vec2: Vector(2, f32) = arr1[1..3].*; + + var slice: []const f32 = &arr1; + var offset: u32 = 1; + // To extract a comptime-known length from a runtime-known offset, + // first extract a new slice from the starting offset, then an array of + // comptime known length + const vec3: Vector(2, f32) = slice[offset..][0..2].*; + try expectEqual(slice[offset], vec2[0]); + try expectEqual(slice[offset + 1], vec2[1]); + try expectEqual(vec2, vec3); +} + {#code_end#} +

+ TODO talk about C ABI interop
+ TODO consider suggesting std.MultiArrayList +

+ {#see_also|@splat|@shuffle|@select|@reduce#} + {#header_close#} {#header_open|Pointers#} @@ -8525,7 +8588,7 @@ test "@hasDecl" {

NaNs are handled as follows: if one of the operands of a (pairwise) operation is NaN, the other operand is returned. If both operands are NaN, NaN is returned.

- {#see_also|@minimum|SIMD|Vectors#} + {#see_also|@minimum|Vectors#} {#header_close#} {#header_open|@memcpy#} @@ -8573,7 +8636,7 @@ mem.set(u8, dest, c);{#endsyntax#}

NaNs are handled as follows: if one of the operands of a (pairwise) operation is NaN, the other operand is returned. If both operands are NaN, NaN is returned.

- {#see_also|@maximum|SIMD|Vectors#} + {#see_also|@maximum|Vectors#} {#header_close#} {#header_open|@wasmMemorySize#} @@ -8779,7 +8842,7 @@ pub const PrefetchOptions = struct {

Selects values element-wise from {#syntax#}a{#endsyntax#} or {#syntax#}b{#endsyntax#} based on {#syntax#}pred{#endsyntax#}. If {#syntax#}pred[i]{#endsyntax#} is {#syntax#}true{#endsyntax#}, the corresponding element in the result will be {#syntax#}a[i]{#endsyntax#} and otherwise {#syntax#}b[i]{#endsyntax#}.

- {#see_also|SIMD|Vectors#} + {#see_also|Vectors#} {#header_close#} {#header_open|@setAlignStack#} @@ -8976,7 +9039,28 @@ test "@setRuntimeSafety" { {#link|pointer|Pointers#}, or {#syntax#}bool{#endsyntax#}. The mask may be any vector length, and its length determines the result length.

- {#see_also|SIMD#} + {#code_begin|test|vector_shuffle#} +const std = @import("std"); +const Vector = std.meta.Vector; +const expect = std.testing.expect; + +test "vector @shuffle" { + const a: Vector(7, u8) = [_]u8{ 'o', 'l', 'h', 'e', 'r', 'z', 'w' }; + const b: Vector(4, u8) = [_]u8{ 'w', 'd', '!', 'x' }; + + // To shuffle within a single vector, pass undefined as the second argument. + // Notice that we can re-order, duplicate, or omit elements of the input vector + const mask1: Vector(5, i32) = [_]i32{ 2, 3, 1, 1, 0 }; + const res1: Vector(5, u8) = @shuffle(u8, a, undefined, mask1); + try expect(std.mem.eql(u8, &@as([5]u8, res1), "hello")); + + // Combining two vectors + const mask2: Vector(6, i32) = [_]i32{ -1, 0, 4, 1, -2, -3 }; + const res2: Vector(6, u8) = @shuffle(u8, a, b, mask2); + try expect(std.mem.eql(u8, &@as([6]u8, res2), "world!")); +} + {#code_end#} + {#see_also|Vectors#} {#header_close#} {#header_open|@sizeOf#}