Commit 8468b544e8

bnprks <bnprks@users.noreply.github.com>
2021-12-19 05:40:57
Add Vector documentation (#10303)
* Create Vector language documentation Main changes to docs: 1. Create brief documentation on Zig vector types with code example 2. Get rid of the SIMD sub-heading under the main Vectors heading, and update links accordingly 3. Add an example to the `@shuffle` docs
1 parent a081373
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#}