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 }