authorgravatar for bnprks@users.noreply.github.combnprks <bnprks@users.noreply.github.com> 2021-12-18 20:40:57-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-12-18 23:40:57-05:00
log8468b544e8769b774e2e5988a58c51d991df999a
treecd154766a9045dc2a28ada367e38b345e2a0504b
parenta08137330c20ce77724a9fc80acf46a7e7978a90
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

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 files changed, 100 insertions(+), 16 deletions(-)

doc/langref.html.in+100-16
...@@ -2483,23 +2483,86 @@ test "null terminated array" {...@@ -2483,23 +2483,86 @@ test "null terminated array" {
2483 {#header_open|Vectors#}2483 {#header_open|Vectors#}
2484 <p>2484 <p>
2485 A vector is a group of booleans, {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on2485 A vector is a group of booleans, {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on
2486 in parallel using a single instruction ({#link|SIMD#}). Vector types are created with the builtin function {#link|@Type#},2486 in parallel using SIMD instructions. Vector types are created with the builtin function {#link|@Type#},
2487 or using the shorthand as {#syntax#}std.meta.Vector{#endsyntax#}.2487 or using the shorthand function {#syntax#}std.meta.Vector{#endsyntax#}.
2488 </p>2488 </p>
2489 <p>2489 <p>
2490 TODO talk about C ABI interop2490 Vectors support the same builtin operators as their underlying base types. These operations are performed
2491 element-wise, and return a vector of the same length as the input vectors. This includes:
2492 <ul>
2493 <li>Arithmetic ({#syntax#}+{#endsyntax#}, {#syntax#}-{#endsyntax#}, {#syntax#}/{#endsyntax#}, {#syntax#}*{#endsyntax#},
2494 {#syntax#}@divFloor{#endsyntax#}, {#syntax#}@sqrt{#endsyntax#}, {#syntax#}@ceil{#endsyntax#},
2495 {#syntax#}@log{#endsyntax#}, etc.)</li>
2496 <li>Bitwise operators ({#syntax#}>>{#endsyntax#}, {#syntax#}<<{#endsyntax#}, {#syntax#}&{#endsyntax#},
2497 {#syntax#}|{#endsyntax#}, {#syntax#}~{#endsyntax#}, etc.)</li>
2498 <li>Comparison operators ({#syntax#}<{#endsyntax#}, {#syntax#}>{#endsyntax#}, {#syntax#}=={#endsyntax#}, etc.)</li>
2499 </ul>
2491 </p>2500 </p>
2492 {#header_open|SIMD#}
2493 <p>2501 <p>
2494 TODO Zig's SIMD abilities are just beginning to be fleshed out. Here are some talking points to update the2502 It is prohibited to use a math operator on a mixture of scalars (individual numbers) and vectors.
2495 docs with:2503 Zig provides the {#link|@splat#} builtin to easily convert from scalars to vectors, and it supports {#link|@reduce#}
2496 * What kind of operations can you do? All the operations on integers and floats? What about mixing scalar and vector?2504 and array indexing syntax to convert from vectors to scalars. Vectors also support assignment to and from
2497 * How to convert to/from vectors/arrays2505 fixed-length arrays with comptime known length.
2498 * How to access individual elements from vectors, how to loop over the elements
2499 * "shuffle"
2500 * Advice on writing high perf software, how to abstract the best way
2501 </p>2506 </p>
2502 {#header_close#}2507 <p>
2508 For rearranging elements within and between vectors, Zig provides the {#link|@shuffle#} and {#link|@select#} functions.
2509 </p>
2510 <p>
2511 Operations on vectors shorter than the target machine's native SIMD size will typically compile to single SIMD
2512 instructions, while vectors longer than the target machine's native SIMD size will compile to multiple SIMD
2513 instructions. If a given operation doesn't have SIMD support on the target architecture, the compiler will default
2514 to operating on each vector element one at a time. Zig supports any comptime-known vector length up to 2^32-1,
2515 although small powers of two (2-64) are most typical. Note that excessively long vector lengths (e.g. 2^20) may
2516 result in compiler crashes on current versions of Zig.
2517 </p>
2518 {#code_begin|test|vector_example#}
2519const std = @import("std");
2520const Vector = std.meta.Vector;
2521const expectEqual = std.testing.expectEqual;
2522
2523test "Basic vector usage" {
2524 // Vectors have a compile-time known length and base type,
2525 // and can be assigned to using array literal syntax
2526 const a: Vector(4, i32) = [_]i32{ 1, 2, 3, 4 };
2527 const b: Vector(4, i32) = [_]i32{ 5, 6, 7, 8 };
2528
2529 // Math operations take place element-wise
2530 const c = a + b;
2531
2532 // Individual vector elements can be accessed using array indexing syntax.
2533 try expectEqual(6, c[0]);
2534 try expectEqual(8, c[1]);
2535 try expectEqual(10, c[2]);
2536 try expectEqual(12, c[3]);
2537}
2538
2539test "Conversion between vectors, arrays, and slices" {
2540 // Vectors and fixed-length arrays can be automatically assigned back and forth
2541 var arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 };
2542 var vec: Vector(4, f32) = arr1;
2543 var arr2: [4]f32 = vec;
2544 try expectEqual(arr1, arr2);
2545
2546 // You can also assign from a slice with comptime-known length to a vector using .*
2547 const vec2: Vector(2, f32) = arr1[1..3].*;
2548
2549 var slice: []const f32 = &arr1;
2550 var offset: u32 = 1;
2551 // To extract a comptime-known length from a runtime-known offset,
2552 // first extract a new slice from the starting offset, then an array of
2553 // comptime known length
2554 const vec3: Vector(2, f32) = slice[offset..][0..2].*;
2555 try expectEqual(slice[offset], vec2[0]);
2556 try expectEqual(slice[offset + 1], vec2[1]);
2557 try expectEqual(vec2, vec3);
2558}
2559 {#code_end#}
2560 <p>
2561 TODO talk about C ABI interop<br>
2562 TODO consider suggesting std.MultiArrayList
2563 </p>
2564 {#see_also|@splat|@shuffle|@select|@reduce#}
2565
2503 {#header_close#}2566 {#header_close#}
25042567
2505 {#header_open|Pointers#}2568 {#header_open|Pointers#}
...@@ -8525,7 +8588,7 @@ test "@hasDecl" {...@@ -8525,7 +8588,7 @@ test "@hasDecl" {
8525 <p>8588 <p>
8526 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.8589 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.
8527 </p>8590 </p>
8528 {#see_also|@minimum|SIMD|Vectors#}8591 {#see_also|@minimum|Vectors#}
8529 {#header_close#}8592 {#header_close#}
85308593
8531 {#header_open|@memcpy#}8594 {#header_open|@memcpy#}
...@@ -8573,7 +8636,7 @@ mem.set(u8, dest, c);{#endsyntax#}</pre>...@@ -8573,7 +8636,7 @@ mem.set(u8, dest, c);{#endsyntax#}</pre>
8573 <p>8636 <p>
8574 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.8637 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.
8575 </p>8638 </p>
8576 {#see_also|@maximum|SIMD|Vectors#}8639 {#see_also|@maximum|Vectors#}
8577 {#header_close#}8640 {#header_close#}
85788641
8579 {#header_open|@wasmMemorySize#}8642 {#header_open|@wasmMemorySize#}
...@@ -8779,7 +8842,7 @@ pub const PrefetchOptions = struct {...@@ -8779,7 +8842,7 @@ pub const PrefetchOptions = struct {
8779 <p>8842 <p>
8780 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#}.8843 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#}.
8781 </p>8844 </p>
8782 {#see_also|SIMD|Vectors#}8845 {#see_also|Vectors#}
8783 {#header_close#}8846 {#header_close#}
87848847
8785 {#header_open|@setAlignStack#}8848 {#header_open|@setAlignStack#}
...@@ -8976,7 +9039,28 @@ test "@setRuntimeSafety" {...@@ -8976,7 +9039,28 @@ test "@setRuntimeSafety" {
8976 {#link|pointer|Pointers#}, or {#syntax#}bool{#endsyntax#}. The mask may be any vector length, and its9039 {#link|pointer|Pointers#}, or {#syntax#}bool{#endsyntax#}. The mask may be any vector length, and its
8977 length determines the result length.9040 length determines the result length.
8978 </p>9041 </p>
8979 {#see_also|SIMD#}9042 {#code_begin|test|vector_shuffle#}
9043const std = @import("std");
9044const Vector = std.meta.Vector;
9045const expect = std.testing.expect;
9046
9047test "vector @shuffle" {
9048 const a: Vector(7, u8) = [_]u8{ 'o', 'l', 'h', 'e', 'r', 'z', 'w' };
9049 const b: Vector(4, u8) = [_]u8{ 'w', 'd', '!', 'x' };
9050
9051 // To shuffle within a single vector, pass undefined as the second argument.
9052 // Notice that we can re-order, duplicate, or omit elements of the input vector
9053 const mask1: Vector(5, i32) = [_]i32{ 2, 3, 1, 1, 0 };
9054 const res1: Vector(5, u8) = @shuffle(u8, a, undefined, mask1);
9055 try expect(std.mem.eql(u8, &@as([5]u8, res1), "hello"));
9056
9057 // Combining two vectors
9058 const mask2: Vector(6, i32) = [_]i32{ -1, 0, 4, 1, -2, -3 };
9059 const res2: Vector(6, u8) = @shuffle(u8, a, b, mask2);
9060 try expect(std.mem.eql(u8, &@as([6]u8, res2), "world!"));
9061}
9062 {#code_end#}
9063 {#see_also|Vectors#}
8980 {#header_close#}9064 {#header_close#}
89819065
8982 {#header_open|@sizeOf#}9066 {#header_open|@sizeOf#}