| ... | ... | @@ -2483,23 +2483,86 @@ test "null terminated array" { |
| 2483 | 2483 | {#header_open|Vectors#} |
| 2484 | 2484 | <p> |
| 2485 | 2485 | 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#}, |
| 2487 | | or using the shorthand as {#syntax#}std.meta.Vector{#endsyntax#}. |
| 2486 | in parallel using SIMD instructions. Vector types are created with the builtin function {#link|@Type#}, |
| 2487 | or using the shorthand function {#syntax#}std.meta.Vector{#endsyntax#}. |
| 2488 | 2488 | </p> |
| 2489 | 2489 | <p> |
| 2490 | | TODO talk about C ABI interop |
| 2490 | 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 | 2500 | </p> |
| 2492 | | {#header_open|SIMD#} |
| 2493 | 2501 | <p> |
| 2494 | | TODO Zig's SIMD abilities are just beginning to be fleshed out. Here are some talking points to update the |
| 2495 | | docs with: |
| 2496 | | * What kind of operations can you do? All the operations on integers and floats? What about mixing scalar and vector? |
| 2497 | | * How to convert to/from vectors/arrays |
| 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 |
| 2502 | It is prohibited to use a math operator on a mixture of scalars (individual numbers) and vectors. |
| 2503 | Zig provides the {#link|@splat#} builtin to easily convert from scalars to vectors, and it supports {#link|@reduce#} |
| 2504 | and array indexing syntax to convert from vectors to scalars. Vectors also support assignment to and from |
| 2505 | fixed-length arrays with comptime known length. |
| 2501 | 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#} |
| 2519 | const std = @import("std"); |
| 2520 | const Vector = std.meta.Vector; |
| 2521 | const expectEqual = std.testing.expectEqual; |
| 2522 | |
| 2523 | test "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 | |
| 2539 | test "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 | 2566 | {#header_close#} |
| 2504 | 2567 | |
| 2505 | 2568 | {#header_open|Pointers#} |
| ... | ... | @@ -8525,7 +8588,7 @@ test "@hasDecl" { |
| 8525 | 8588 | <p> |
| 8526 | 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 | 8590 | </p> |
| 8528 | | {#see_also|@minimum|SIMD|Vectors#} |
| 8591 | {#see_also|@minimum|Vectors#} |
| 8529 | 8592 | {#header_close#} |
| 8530 | 8593 | |
| 8531 | 8594 | {#header_open|@memcpy#} |
| ... | ... | @@ -8573,7 +8636,7 @@ mem.set(u8, dest, c);{#endsyntax#}</pre> |
| 8573 | 8636 | <p> |
| 8574 | 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 | 8638 | </p> |
| 8576 | | {#see_also|@maximum|SIMD|Vectors#} |
| 8639 | {#see_also|@maximum|Vectors#} |
| 8577 | 8640 | {#header_close#} |
| 8578 | 8641 | |
| 8579 | 8642 | {#header_open|@wasmMemorySize#} |
| ... | ... | @@ -8779,7 +8842,7 @@ pub const PrefetchOptions = struct { |
| 8779 | 8842 | <p> |
| 8780 | 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 | 8844 | </p> |
| 8782 | | {#see_also|SIMD|Vectors#} |
| 8845 | {#see_also|Vectors#} |
| 8783 | 8846 | {#header_close#} |
| 8784 | 8847 | |
| 8785 | 8848 | {#header_open|@setAlignStack#} |
| ... | ... | @@ -8976,7 +9039,28 @@ test "@setRuntimeSafety" { |
| 8976 | 9039 | {#link|pointer|Pointers#}, or {#syntax#}bool{#endsyntax#}. The mask may be any vector length, and its |
| 8977 | 9040 | length determines the result length. |
| 8978 | 9041 | </p> |
| 8979 | | {#see_also|SIMD#} |
| 9042 | {#code_begin|test|vector_shuffle#} |
| 9043 | const std = @import("std"); |
| 9044 | const Vector = std.meta.Vector; |
| 9045 | const expect = std.testing.expect; |
| 9046 | |
| 9047 | test "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 | 9064 | {#header_close#} |
| 8981 | 9065 | |
| 8982 | 9066 | {#header_open|@sizeOf#} |