lib/simd/src/construct.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub fn zero(comptime D: type) D.Vector {
4 return @splat(0);
5 }
6
7 pub fn set(comptime D: type, value: D.Lane) D.Vector {
8 return @splat(value);
9 }
10
11 pub fn @"undefined"(comptime D: type) D.Vector {
12 return undefined;
13 }
14
15 pub fn dup128VecFromValues(
16 comptime D: type,
17 values: [128 / @bitSizeOf(D.Lane)]D.Lane,
18 ) D.Vector {
19 const lanes_per_block = 128 / @bitSizeOf(D.Lane);
20 var result: D.Vector = undefined;
21 inline for (0..D.lane_count) |index| {
22 result[index] = values[index % lanes_per_block];
23 }
24 return result;
25 }
26
27 pub fn maskedSetOr(
28 comptime D: type,
29 inactive: D.Vector,
30 mask: D.Mask,
31 value: D.Lane,
32 ) D.Vector {
33 return @select(D.Lane, mask, @as(D.Vector, @splat(value)), inactive);
34 }
35
36 pub fn maskedSet(comptime D: type, mask: D.Mask, value: D.Lane) D.Vector {
37 return maskedSetOr(D, @splat(0), mask, value);
38 }
39
40 pub fn inf(comptime D: type) D.Vector {
41 if (@typeInfo(D.Lane) != .float) @compileError("inf requires floating-point lanes");
42 return @splat(std.math.inf(D.Lane));
43 }
44
45 pub fn nan(comptime D: type) D.Vector {
46 if (@typeInfo(D.Lane) != .float) @compileError("nan requires floating-point lanes");
47 return @splat(std.math.nan(D.Lane));
48 }
49
50 pub fn iota(comptime D: type, first: D.Lane) D.Vector {
51 var result: D.Vector = undefined;
52 inline for (0..D.lane_count) |index| {
53 result[index] = iotaLane(D.Lane, first, index);
54 }
55 return result;
56 }
57
58 pub fn firstN(comptime D: type, count: usize) D.Mask {
59 var result: D.Mask = @splat(false);
60 inline for (0..D.lane_count) |index| {
61 result[index] = index < count;
62 }
63 return result;
64 }
65
66 pub fn lane(comptime D: type, value: D.Vector, index: usize) D.Lane {
67 std.debug.assert(index < D.lane_count);
68 const values: [D.lane_count]D.Lane = value;
69 return values[index];
70 }
71
72 pub fn insertLane(
73 comptime D: type,
74 value: D.Vector,
75 index: usize,
76 inserted: D.Lane,
77 ) D.Vector {
78 std.debug.assert(index < D.lane_count);
79 var values: [D.lane_count]D.Lane = value;
80 values[index] = inserted;
81 std.debug.assert(values[index] == inserted);
82 return values;
83 }
84
85 fn iotaLane(comptime T: type, first: T, index: usize) T {
86 return switch (@typeInfo(T)) {
87 .int => blk: {
88 const U = @Int(.unsigned, @bitSizeOf(T));
89 const first_bits: U = @bitCast(first);
90 const offset: U = @truncate(index);
91 break :blk @bitCast(first_bits +% offset);
92 },
93 .float => first + @as(T, @floatFromInt(index)),
94 else => unreachable,
95 };
96 }
97
98 test "constructors reproduce Highway set and wrapping iota" {
99 const simd = @import("root.zig");
100 const D = simd.FixedTag(i8, 4);
101 try std.testing.expect(@reduce(.And, set(D, 7) == @as(D.Vector, @splat(7))));
102 const expected: D.Vector = .{ 126, 127, -128, -127 };
103 try std.testing.expect(@reduce(.And, iota(D, 126) == expected));
104 try std.testing.expect(@reduce(.And, zero(D) == @as(D.Vector, @splat(0))));
105 }
106
107 test "firstN clamps naturally and lane insertion is isolated" {
108 const simd = @import("root.zig");
109 const D = simd.FixedTag(u32, 4);
110 const all = firstN(D, 9);
111 try std.testing.expect(@reduce(.And, all));
112 const only_two: D.Mask = .{ true, true, false, false };
113 try std.testing.expect(@reduce(.And, firstN(D, 2) == only_two));
114 const changed = insertLane(D, zero(D), 2, 9);
115 try std.testing.expectEqual(@as(u32, 9), lane(D, changed, 2));
116 try std.testing.expectEqual(@as(u32, 0), lane(D, changed, 1));
117 }
118
119 test "floating constants classify as Highway infinity and NaN" {
120 const simd = @import("root.zig");
121 inline for (.{ f16, f32, f64 }) |T| {
122 const D = simd.FixedTag(T, 4);
123 try std.testing.expect(@reduce(.And, inf(D) == @as(D.Vector, @splat(std.math.inf(T)))));
124 try std.testing.expect(@reduce(.And, nan(D) != nan(D)));
125 }
126 }
127
128 test "Highway block constants and masked sets preserve their lane contracts" {
129 const simd = @import("root.zig");
130 const D = simd.FixedTag(u32, 8);
131 const repeated = dup128VecFromValues(D, .{ 1, 2, 3, 4 });
132 try std.testing.expect(@reduce(.And, repeated == @as(D.Vector, .{ 1, 2, 3, 4, 1, 2, 3, 4 })));
133 const mask: D.Mask = .{ true, false, true, false, false, true, false, true };
134 try std.testing.expect(@reduce(.And, maskedSet(D, mask, 7) ==
135 @as(D.Vector, .{ 7, 0, 7, 0, 0, 7, 0, 7 })));
136 try std.testing.expect(@reduce(.And, maskedSetOr(D, @splat(9), mask, 7) ==
137 @as(D.Vector, .{ 7, 9, 7, 9, 9, 7, 9, 7 })));
138 }