Commit 8468b544e8
Changed files (1)
doc/langref.html.in
@@ -2483,23 +2483,86 @@ test "null terminated array" {
{#header_open|Vectors#}
<p>
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#}.
</p>
<p>
- 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:
+ <ul>
+ <li>Arithmetic ({#syntax#}+{#endsyntax#}, {#syntax#}-{#endsyntax#}, {#syntax#}/{#endsyntax#}, {#syntax#}*{#endsyntax#},
+ {#syntax#}@divFloor{#endsyntax#}, {#syntax#}@sqrt{#endsyntax#}, {#syntax#}@ceil{#endsyntax#},
+ {#syntax#}@log{#endsyntax#}, etc.)</li>
+ <li>Bitwise operators ({#syntax#}>>{#endsyntax#}, {#syntax#}<<{#endsyntax#}, {#syntax#}&{#endsyntax#},
+ {#syntax#}|{#endsyntax#}, {#syntax#}~{#endsyntax#}, etc.)</li>
+ <li>Comparison operators ({#syntax#}<{#endsyntax#}, {#syntax#}>{#endsyntax#}, {#syntax#}=={#endsyntax#}, etc.)</li>
+ </ul>
</p>
- {#header_open|SIMD#}
<p>
- 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.
</p>
- {#header_close#}
+ <p>
+ For rearranging elements within and between vectors, Zig provides the {#link|@shuffle#} and {#link|@select#} functions.
+ </p>
+ <p>
+ 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.
+ </p>
+ {#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#}
+ <p>
+ TODO talk about C ABI interop<br>
+ TODO consider suggesting std.MultiArrayList
+ </p>
+ {#see_also|@splat|@shuffle|@select|@reduce#}
+
{#header_close#}
{#header_open|Pointers#}
@@ -8525,7 +8588,7 @@ test "@hasDecl" {
<p>
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.
</p>
- {#see_also|@minimum|SIMD|Vectors#}
+ {#see_also|@minimum|Vectors#}
{#header_close#}
{#header_open|@memcpy#}
@@ -8573,7 +8636,7 @@ mem.set(u8, dest, c);{#endsyntax#}</pre>
<p>
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.
</p>
- {#see_also|@maximum|SIMD|Vectors#}
+ {#see_also|@maximum|Vectors#}
{#header_close#}
{#header_open|@wasmMemorySize#}
@@ -8779,7 +8842,7 @@ pub const PrefetchOptions = struct {
<p>
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#}.
</p>
- {#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.
</p>
- {#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#}