lib/simd/src/block.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub fn broadcast(comptime D: type, comptime lane: usize, value: D.Vector) D.Vector {
4 const lanes_per_block = comptime blockLanes(D);
5 if (comptime lane >= lanes_per_block) @compileError("broadcast lane is outside a 128-bit block");
6 const lanes: [D.lane_count]D.Lane = @bitCast(value);
7 var result: [D.lane_count]D.Lane = undefined;
8 inline for (0..D.lane_count) |index| {
9 result[index] = lanes[(index / lanes_per_block) * lanes_per_block + lane];
10 }
11 return @bitCast(result);
12 }
13
14 pub fn interleaveLower(comptime D: type, a: D.Vector, b: D.Vector) D.Vector {
15 return interleaveHalf(D, false, a, b);
16 }
17
18 pub fn interleaveUpper(comptime D: type, a: D.Vector, b: D.Vector) D.Vector {
19 if (comptime blockLanes(D) < 2) @compileError("interleaveUpper requires at least two lanes per block");
20 return interleaveHalf(D, true, a, b);
21 }
22
23 pub fn interleaveEven(comptime D: type, a: D.Vector, b: D.Vector) D.Vector {
24 return interleaveParity(D, 0, a, b);
25 }
26
27 pub fn interleaveOdd(comptime D: type, a: D.Vector, b: D.Vector) D.Vector {
28 if (comptime blockLanes(D) < 2) @compileError("interleaveOdd requires at least two lanes per block");
29 return interleaveParity(D, 1, a, b);
30 }
31
32 pub fn zipLower(comptime D: type, a: D.Vector, b: D.Vector) D.repartition(wideInteger(D.Lane)).Vector {
33 return @bitCast(interleaveLower(D, a, b));
34 }
35
36 pub fn zipUpper(comptime D: type, a: D.Vector, b: D.Vector) D.repartition(wideInteger(D.Lane)).Vector {
37 return @bitCast(interleaveUpper(D, a, b));
38 }
39
40 pub fn shiftLeftBytes(comptime D: type, comptime amount: usize, value: D.Vector) D.Vector {
41 return shiftBytes(D, amount, true, value);
42 }
43
44 pub fn shiftRightBytes(comptime D: type, comptime amount: usize, value: D.Vector) D.Vector {
45 return shiftBytes(D, amount, false, value);
46 }
47
48 pub fn shiftLeftLanes(comptime D: type, comptime amount: usize, value: D.Vector) D.Vector {
49 return shiftLanes(D, amount, true, value);
50 }
51
52 pub fn shiftRightLanes(comptime D: type, comptime amount: usize, value: D.Vector) D.Vector {
53 return shiftLanes(D, amount, false, value);
54 }
55
56 pub fn combineShiftRightBytes(
57 comptime D: type,
58 comptime amount: usize,
59 high: D.Vector,
60 low: D.Vector,
61 ) D.Vector {
62 const bytes_per_block = @min(D.byte_count, 16);
63 if (comptime amount >= bytes_per_block) @compileError("combined byte shift must remain inside a block");
64 const high_bytes: [D.byte_count]u8 = @bitCast(high);
65 const low_bytes: [D.byte_count]u8 = @bitCast(low);
66 var result: [D.byte_count]u8 = undefined;
67 inline for (0..D.byte_count) |index| {
68 const base = index / bytes_per_block * bytes_per_block;
69 const source = index % bytes_per_block + amount;
70 result[index] = if (source < bytes_per_block)
71 low_bytes[base + source]
72 else
73 high_bytes[base + source - bytes_per_block];
74 }
75 return @bitCast(result);
76 }
77
78 pub fn combineShiftRightLanes(
79 comptime D: type,
80 comptime amount: usize,
81 high: D.Vector,
82 low: D.Vector,
83 ) D.Vector {
84 const lanes_per_block = comptime blockLanes(D);
85 if (comptime amount >= lanes_per_block) @compileError("combined lane shift must remain inside a block");
86 const high_lanes: [D.lane_count]D.Lane = @bitCast(high);
87 const low_lanes: [D.lane_count]D.Lane = @bitCast(low);
88 var result: [D.lane_count]D.Lane = undefined;
89 inline for (0..D.lane_count) |index| {
90 const base = index / lanes_per_block * lanes_per_block;
91 const source = index % lanes_per_block + amount;
92 result[index] = if (source < lanes_per_block)
93 low_lanes[base + source]
94 else
95 high_lanes[base + source - lanes_per_block];
96 }
97 return @bitCast(result);
98 }
99
100 pub fn per4LaneBlockShuffle(
101 comptime D: type,
102 comptime index3: usize,
103 comptime index2: usize,
104 comptime index1: usize,
105 comptime index0: usize,
106 value: D.Vector,
107 ) D.Vector {
108 inline for (.{ index0, index1, index2, index3 }) |index| {
109 if (comptime index >= 4) @compileError("per-four-lane shuffle index must be below four");
110 }
111 const group = @min(D.lane_count, 4);
112 const indices = [4]usize{ index0, index1, index2, index3 };
113 const lanes: [D.lane_count]D.Lane = @bitCast(value);
114 var result: [D.lane_count]D.Lane = undefined;
115 inline for (0..D.lane_count) |lane| {
116 const source = indices[lane % group];
117 result[lane] = if (source < group) lanes[lane / group * group + source] else lanes[lane];
118 }
119 return @bitCast(result);
120 }
121
122 pub fn shuffle1032(comptime D: type, value: D.Vector) D.Vector {
123 validate32BitLanes(D);
124 return per4LaneBlockShuffle(D, 1, 0, 3, 2, value);
125 }
126
127 pub fn shuffle0321(comptime D: type, value: D.Vector) D.Vector {
128 validate32BitLanes(D);
129 return per4LaneBlockShuffle(D, 0, 3, 2, 1, value);
130 }
131
132 pub fn shuffle2103(comptime D: type, value: D.Vector) D.Vector {
133 validate32BitLanes(D);
134 return per4LaneBlockShuffle(D, 2, 1, 0, 3, value);
135 }
136
137 pub fn shuffle2301(comptime D: type, value: D.Vector) D.Vector {
138 validate32BitLanes(D);
139 return per4LaneBlockShuffle(D, 2, 3, 0, 1, value);
140 }
141
142 pub fn shuffle01(comptime D: type, value: D.Vector) D.Vector {
143 if (comptime @bitSizeOf(D.Lane) != 64) @compileError("shuffle01 requires 64-bit lanes");
144 const lanes: [D.lane_count]D.Lane = value;
145 var result: [D.lane_count]D.Lane = undefined;
146 inline for (0..D.lane_count) |index| result[index] = lanes[index ^ 1];
147 return result;
148 }
149
150 pub fn shuffle0123(comptime D: type, value: D.Vector) D.Vector {
151 validate32BitLanes(D);
152 return per4LaneBlockShuffle(D, 0, 1, 2, 3, value);
153 }
154
155 pub fn blocks(comptime D: type) usize {
156 return @max(1, D.byte_count / 16);
157 }
158
159 pub fn extractBlock(
160 comptime D: type,
161 comptime block_index: usize,
162 value: D.Vector,
163 ) @Vector(blockLanes(D), D.Lane) {
164 const block_count = comptime blocks(D);
165 if (comptime block_index >= block_count) @compileError("block index is outside the vector");
166 const lanes_per_block = comptime blockLanes(D);
167 const lanes: [D.lane_count]D.Lane = @bitCast(value);
168 var result: [lanes_per_block]D.Lane = undefined;
169 inline for (0..lanes_per_block) |lane| {
170 result[lane] = lanes[block_index * lanes_per_block + lane];
171 }
172 return @bitCast(result);
173 }
174
175 pub fn insertBlock(
176 comptime D: type,
177 comptime block_index: usize,
178 value: D.Vector,
179 inserted: @Vector(blockLanes(D), D.Lane),
180 ) D.Vector {
181 const block_count = comptime blocks(D);
182 if (comptime block_index >= block_count) @compileError("block index is outside the vector");
183 const lanes_per_block = comptime blockLanes(D);
184 var result: [D.lane_count]D.Lane = @bitCast(value);
185 const inserted_lanes: [lanes_per_block]D.Lane = @bitCast(inserted);
186 inline for (0..lanes_per_block) |lane| {
187 result[block_index * lanes_per_block + lane] = inserted_lanes[lane];
188 }
189 return @bitCast(result);
190 }
191
192 pub fn broadcastBlock(comptime D: type, comptime block_index: usize, value: D.Vector) D.Vector {
193 const block_count = comptime blocks(D);
194 if (comptime block_index >= block_count) @compileError("block index is outside the vector");
195 if (block_count == 1) return value;
196 const lanes_per_block = comptime blockLanes(D);
197 const lanes: [D.lane_count]D.Lane = @bitCast(value);
198 var result: [D.lane_count]D.Lane = undefined;
199 inline for (0..D.lane_count) |lane| {
200 result[lane] = lanes[block_index * lanes_per_block + lane % lanes_per_block];
201 }
202 return @bitCast(result);
203 }
204
205 pub fn oddEvenBlocks(comptime D: type, odd: D.Vector, even: D.Vector) D.Vector {
206 const block_count = comptime blocks(D);
207 if (block_count == 1) return even;
208 const lanes_per_block = comptime blockLanes(D);
209 const odd_lanes: [D.lane_count]D.Lane = @bitCast(odd);
210 const even_lanes: [D.lane_count]D.Lane = @bitCast(even);
211 var result: [D.lane_count]D.Lane = undefined;
212 inline for (0..D.lane_count) |lane| {
213 result[lane] = if ((lane / lanes_per_block) & 1 == 0) even_lanes[lane] else odd_lanes[lane];
214 }
215 return @bitCast(result);
216 }
217
218 pub fn swapAdjacentBlocks(comptime D: type, value: D.Vector) D.Vector {
219 validateMultipleBlocks(D);
220 const lanes_per_block = comptime blockLanes(D);
221 const lanes: [D.lane_count]D.Lane = @bitCast(value);
222 var result: [D.lane_count]D.Lane = undefined;
223 inline for (0..D.lane_count) |lane| {
224 const block_index = lane / lanes_per_block;
225 result[lane] = lanes[(block_index ^ 1) * lanes_per_block + lane % lanes_per_block];
226 }
227 return @bitCast(result);
228 }
229
230 pub fn interleaveEvenBlocks(comptime D: type, a: D.Vector, b: D.Vector) D.Vector {
231 return interleaveBlockParity(D, 0, a, b);
232 }
233
234 pub fn interleaveOddBlocks(comptime D: type, a: D.Vector, b: D.Vector) D.Vector {
235 return interleaveBlockParity(D, 1, a, b);
236 }
237
238 pub fn interleaveLowerBlocks(comptime D: type, a: D.Vector, b: D.Vector) D.Vector {
239 return interleaveBlockHalf(D, false, a, b);
240 }
241
242 pub fn interleaveUpperBlocks(comptime D: type, a: D.Vector, b: D.Vector) D.Vector {
243 return interleaveBlockHalf(D, true, a, b);
244 }
245
246 fn interleaveHalf(comptime D: type, comptime upper: bool, a: D.Vector, b: D.Vector) D.Vector {
247 const lanes_per_block = comptime blockLanes(D);
248 if (lanes_per_block == 1) return a;
249 const a_lanes: [D.lane_count]D.Lane = @bitCast(a);
250 const b_lanes: [D.lane_count]D.Lane = @bitCast(b);
251 var result: [D.lane_count]D.Lane = undefined;
252 inline for (0..D.lane_count) |index| {
253 const base = index / lanes_per_block * lanes_per_block;
254 const within = index % lanes_per_block;
255 const source = base + within / 2 + if (upper) lanes_per_block / 2 else 0;
256 result[index] = if (within & 1 == 0) a_lanes[source] else b_lanes[source];
257 }
258 return @bitCast(result);
259 }
260
261 fn interleaveBlockParity(
262 comptime D: type,
263 comptime parity: usize,
264 a: D.Vector,
265 b: D.Vector,
266 ) D.Vector {
267 validateMultipleBlocks(D);
268 const lanes_per_block = comptime blockLanes(D);
269 const a_lanes: [D.lane_count]D.Lane = @bitCast(a);
270 const b_lanes: [D.lane_count]D.Lane = @bitCast(b);
271 var result: [D.lane_count]D.Lane = undefined;
272 inline for (0..D.lane_count) |lane| {
273 const output_block = lane / lanes_per_block;
274 const source_block = (output_block / 2) * 2 + parity;
275 const source_lane = source_block * lanes_per_block + lane % lanes_per_block;
276 result[lane] = if (output_block & 1 == 0) a_lanes[source_lane] else b_lanes[source_lane];
277 }
278 return @bitCast(result);
279 }
280
281 fn interleaveBlockHalf(
282 comptime D: type,
283 comptime upper: bool,
284 a: D.Vector,
285 b: D.Vector,
286 ) D.Vector {
287 validateMultipleBlocks(D);
288 const block_count = comptime blocks(D);
289 const lanes_per_block = comptime blockLanes(D);
290 const a_lanes: [D.lane_count]D.Lane = @bitCast(a);
291 const b_lanes: [D.lane_count]D.Lane = @bitCast(b);
292 var result: [D.lane_count]D.Lane = undefined;
293 inline for (0..D.lane_count) |lane| {
294 const output_block = lane / lanes_per_block;
295 const source_block = output_block / 2 + if (upper) block_count / 2 else 0;
296 const source_lane = source_block * lanes_per_block + lane % lanes_per_block;
297 result[lane] = if (output_block & 1 == 0) a_lanes[source_lane] else b_lanes[source_lane];
298 }
299 return @bitCast(result);
300 }
301
302 fn interleaveParity(
303 comptime D: type,
304 comptime parity: usize,
305 a: D.Vector,
306 b: D.Vector,
307 ) D.Vector {
308 const lanes_per_block = comptime blockLanes(D);
309 const a_lanes: [D.lane_count]D.Lane = @bitCast(a);
310 const b_lanes: [D.lane_count]D.Lane = @bitCast(b);
311 var result: [D.lane_count]D.Lane = undefined;
312 inline for (0..D.lane_count) |index| {
313 const base = index / lanes_per_block * lanes_per_block;
314 const within = index % lanes_per_block;
315 const source = base + (within / 2) * 2 + parity;
316 result[index] = if (within & 1 == 0) a_lanes[source] else b_lanes[source];
317 }
318 return @bitCast(result);
319 }
320
321 fn shiftBytes(comptime D: type, comptime amount: usize, comptime left: bool, value: D.Vector) D.Vector {
322 const bytes_per_block = @min(D.byte_count, 16);
323 if (comptime amount > bytes_per_block) @compileError("byte shift exceeds a block");
324 const bytes: [D.byte_count]u8 = @bitCast(value);
325 var result: [D.byte_count]u8 = @splat(0);
326 inline for (0..D.byte_count) |index| {
327 const within = index % bytes_per_block;
328 if (left) {
329 if (within >= amount) result[index] = bytes[index - amount];
330 } else if (within + amount < bytes_per_block) {
331 result[index] = bytes[index + amount];
332 }
333 }
334 return @bitCast(result);
335 }
336
337 fn shiftLanes(comptime D: type, comptime amount: usize, comptime left: bool, value: D.Vector) D.Vector {
338 const lanes_per_block = comptime blockLanes(D);
339 if (comptime amount > lanes_per_block) @compileError("lane shift exceeds a block");
340 const lanes: [D.lane_count]D.Lane = @bitCast(value);
341 var result = @as([D.lane_count]D.Lane, @splat(0));
342 inline for (0..D.lane_count) |index| {
343 const within = index % lanes_per_block;
344 if (left) {
345 if (within >= amount) result[index] = lanes[index - amount];
346 } else if (within + amount < lanes_per_block) {
347 result[index] = lanes[index + amount];
348 }
349 }
350 return @bitCast(result);
351 }
352
353 fn blockLanes(comptime D: type) comptime_int {
354 return @min(D.lane_count, 16 / @sizeOf(D.Lane));
355 }
356
357 fn validateMultipleBlocks(comptime D: type) void {
358 if (comptime blocks(D) < 2) @compileError("operation requires at least two 128-bit blocks");
359 }
360
361 fn validate32BitLanes(comptime D: type) void {
362 if (comptime @bitSizeOf(D.Lane) != 32) @compileError("shuffle requires 32-bit lanes");
363 }
364
365 fn wideInteger(comptime T: type) type {
366 return switch (T) {
367 i8 => i16,
368 u8 => u16,
369 i16 => i32,
370 u16 => u32,
371 i32 => i64,
372 u32 => u64,
373 else => @compileError("zip requires 8-, 16-, or 32-bit integer lanes"),
374 };
375 }
376
377 test "Highway block broadcast and interleave restart at 128-bit boundaries" {
378 const simd = @import("root.zig");
379 const D = simd.FixedTag(u32, 8);
380 const a: D.Vector = .{ 0, 2, 4, 6, 8, 10, 12, 14 };
381 const b: D.Vector = .{ 1, 3, 5, 7, 9, 11, 13, 15 };
382 try std.testing.expect(@reduce(.And, broadcast(D, 2, a) == @as(D.Vector, .{ 4, 4, 4, 4, 12, 12, 12, 12 })));
383 try std.testing.expect(@reduce(.And, interleaveLower(D, a, b) == @as(D.Vector, .{ 0, 1, 2, 3, 8, 9, 10, 11 })));
384 try std.testing.expect(@reduce(.And, interleaveUpper(D, a, b) == @as(D.Vector, .{ 4, 5, 6, 7, 12, 13, 14, 15 })));
385 try std.testing.expect(@reduce(.And, interleaveEven(D, a, b) == @as(D.Vector, .{ 0, 1, 4, 5, 8, 9, 12, 13 })));
386 try std.testing.expect(@reduce(.And, interleaveOdd(D, a, b) == @as(D.Vector, .{ 2, 3, 6, 7, 10, 11, 14, 15 })));
387 }
388
389 test "Highway zip retains interleaved bits in wide lanes" {
390 const simd = @import("root.zig");
391 const D = simd.FixedTag(u16, 8);
392 const a: D.Vector = .{ 0, 2, 4, 6, 8, 10, 12, 14 };
393 const b: D.Vector = .{ 1, 3, 5, 7, 9, 11, 13, 15 };
394 const W = D.repartition(u32);
395 try std.testing.expect(@reduce(.And, zipLower(D, a, b) == @as(W.Vector, .{
396 0x0001_0000, 0x0003_0002, 0x0005_0004, 0x0007_0006,
397 })));
398 try std.testing.expect(@reduce(.And, zipUpper(D, a, b) == @as(W.Vector, .{
399 0x0009_0008, 0x000b_000a, 0x000d_000c, 0x000f_000e,
400 })));
401 }
402
403 test "Highway block shifts zero fill and combined shifts concatenate low then high" {
404 const simd = @import("root.zig");
405 const D = simd.FixedTag(u32, 8);
406 const low: D.Vector = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
407 const high: D.Vector = .{ 10, 11, 12, 13, 14, 15, 16, 17 };
408 try std.testing.expect(@reduce(.And, shiftLeftLanes(D, 1, low) == @as(D.Vector, .{ 0, 0, 1, 2, 0, 4, 5, 6 })));
409 try std.testing.expect(@reduce(.And, shiftRightLanes(D, 1, low) == @as(D.Vector, .{ 1, 2, 3, 0, 5, 6, 7, 0 })));
410 try std.testing.expect(@reduce(.And, combineShiftRightLanes(D, 2, high, low) == @as(D.Vector, .{ 2, 3, 10, 11, 6, 7, 14, 15 })));
411
412 const B = simd.FixedTag(u8, 32);
413 const bytes = simd.iota(B, 1);
414 try std.testing.expectEqual(@as(u8, 0), shiftLeftBytes(B, 1, bytes)[0]);
415 try std.testing.expectEqual(@as(u8, 1), shiftLeftBytes(B, 1, bytes)[1]);
416 try std.testing.expectEqual(@as(u8, 0), shiftLeftBytes(B, 1, bytes)[16]);
417 try std.testing.expectEqual(@as(u8, 2), shiftRightBytes(B, 1, bytes)[0]);
418 try std.testing.expectEqual(@as(u8, 0), shiftRightBytes(B, 1, bytes)[15]);
419 }
420
421 test "Highway per-four-lane shuffle repeats its pattern" {
422 const simd = @import("root.zig");
423 const D = simd.FixedTag(i32, 8);
424 const value: D.Vector = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
425 try std.testing.expect(@reduce(.And, per4LaneBlockShuffle(D, 0, 1, 2, 3, value) ==
426 @as(D.Vector, .{ 3, 2, 1, 0, 7, 6, 5, 4 })));
427 try std.testing.expect(@reduce(.And, shuffle1032(D, value) == @as(D.Vector, .{ 2, 3, 0, 1, 6, 7, 4, 5 })));
428 try std.testing.expect(@reduce(.And, shuffle0321(D, value) == @as(D.Vector, .{ 1, 2, 3, 0, 5, 6, 7, 4 })));
429 try std.testing.expect(@reduce(.And, shuffle2103(D, value) == @as(D.Vector, .{ 3, 0, 1, 2, 7, 4, 5, 6 })));
430 try std.testing.expect(@reduce(.And, shuffle2301(D, value) == @as(D.Vector, .{ 1, 0, 3, 2, 5, 4, 7, 6 })));
431 try std.testing.expect(@reduce(.And, shuffle0123(D, value) == @as(D.Vector, .{ 3, 2, 1, 0, 7, 6, 5, 4 })));
432 const D64 = simd.FixedTag(u64, 4);
433 try std.testing.expect(@reduce(.And, shuffle01(D64, .{ 0, 1, 2, 3 }) ==
434 @as(D64.Vector, .{ 1, 0, 3, 2 })));
435 }
436
437 test "Highway block extraction insertion broadcast and selection retain block boundaries" {
438 const simd = @import("root.zig");
439 const D = simd.FixedTag(u32, 16);
440 const value: D.Vector = .{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
441 const replacement: @Vector(4, u32) = .{ 90, 91, 92, 93 };
442 try std.testing.expectEqual(@as(usize, 4), blocks(D));
443 try std.testing.expect(@reduce(.And, extractBlock(D, 2, value) == replacement - @as(@Vector(4, u32), @splat(82))));
444 try std.testing.expect(@reduce(.And, insertBlock(D, 1, value, replacement) == @as(D.Vector, .{
445 0, 1, 2, 3, 90, 91, 92, 93, 8, 9, 10, 11, 12, 13, 14, 15,
446 })));
447 try std.testing.expect(@reduce(.And, broadcastBlock(D, 2, value) == @as(D.Vector, .{
448 8, 9, 10, 11, 8, 9, 10, 11, 8, 9, 10, 11, 8, 9, 10, 11,
449 })));
450 try std.testing.expect(@reduce(.And, swapAdjacentBlocks(D, value) == @as(D.Vector, .{
451 4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11,
452 })));
453 }
454
455 test "Highway block interleaves select even odd lower and upper source blocks" {
456 const simd = @import("root.zig");
457 const D = simd.FixedTag(u32, 16);
458 const a: D.Vector = .{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
459 const b = a + @as(D.Vector, @splat(100));
460 try std.testing.expect(@reduce(.And, oddEvenBlocks(D, b, a) == @as(D.Vector, .{
461 0, 1, 2, 3, 104, 105, 106, 107, 8, 9, 10, 11, 112, 113, 114, 115,
462 })));
463 try std.testing.expect(@reduce(.And, interleaveEvenBlocks(D, a, b) == @as(D.Vector, .{
464 0, 1, 2, 3, 100, 101, 102, 103, 8, 9, 10, 11, 108, 109, 110, 111,
465 })));
466 try std.testing.expect(@reduce(.And, interleaveOddBlocks(D, a, b) == @as(D.Vector, .{
467 4, 5, 6, 7, 104, 105, 106, 107, 12, 13, 14, 15, 112, 113, 114, 115,
468 })));
469 try std.testing.expect(@reduce(.And, interleaveLowerBlocks(D, a, b) == @as(D.Vector, .{
470 0, 1, 2, 3, 100, 101, 102, 103, 4, 5, 6, 7, 104, 105, 106, 107,
471 })));
472 try std.testing.expect(@reduce(.And, interleaveUpperBlocks(D, a, b) == @as(D.Vector, .{
473 8, 9, 10, 11, 108, 109, 110, 111, 12, 13, 14, 15, 112, 113, 114, 115,
474 })));
475 }
476
477 fn verifyBlockLaneType(comptime T: type) !void {
478 const simd = @import("root.zig");
479 const D = simd.FixedTag(T, 32 / @sizeOf(T));
480 const B = @Vector(16 / @sizeOf(T), T);
481 const value: D.Vector = @splat(0);
482 const block_value: B = @splat(0);
483 try std.testing.expect(@reduce(.And, broadcast(D, 0, value) == value));
484 try std.testing.expect(@reduce(.And, interleaveLower(D, value, value) == value));
485 try std.testing.expect(@reduce(.And, interleaveUpper(D, value, value) == value));
486 try std.testing.expect(@reduce(.And, interleaveEven(D, value, value) == value));
487 try std.testing.expect(@reduce(.And, interleaveOdd(D, value, value) == value));
488 try std.testing.expect(@reduce(.And, shiftLeftLanes(D, 1, value) == value));
489 try std.testing.expect(@reduce(.And, shiftRightLanes(D, 1, value) == value));
490 try std.testing.expect(@reduce(.And, combineShiftRightLanes(D, 1, value, value) == value));
491 if (comptime @typeInfo(T) == .int) {
492 try std.testing.expect(@reduce(.And, shiftLeftBytes(D, 1, value) == value));
493 try std.testing.expect(@reduce(.And, shiftRightBytes(D, 1, value) == value));
494 try std.testing.expect(@reduce(.And, combineShiftRightBytes(D, 1, value, value) == value));
495 }
496 try std.testing.expect(@reduce(.And, insertBlock(D, 1, value, block_value) == value));
497 try std.testing.expect(@reduce(.And, extractBlock(D, 1, value) == block_value));
498 try std.testing.expect(@reduce(.And, broadcastBlock(D, 1, value) == value));
499 try std.testing.expect(@reduce(.And, oddEvenBlocks(D, value, value) == value));
500 try std.testing.expect(@reduce(.And, swapAdjacentBlocks(D, value) == value));
501 try std.testing.expect(@reduce(.And, interleaveEvenBlocks(D, value, value) == value));
502 try std.testing.expect(@reduce(.And, interleaveOddBlocks(D, value, value) == value));
503 try std.testing.expect(@reduce(.And, interleaveLowerBlocks(D, value, value) == value));
504 try std.testing.expect(@reduce(.And, interleaveUpperBlocks(D, value, value) == value));
505 }
506
507 test "Highway block geometry instantiates every lane type" {
508 inline for (.{ u8, i8, u16, i16, u32, i32, u64, i64, f16, f32, f64 }) |T| {
509 try verifyBlockLaneType(T);
510 }
511 inline for (.{ u8, i8, u16, i16, u32, i32 }) |T| {
512 const simd = @import("root.zig");
513 const D = simd.FixedTag(T, 16 / @sizeOf(T));
514 const value: D.Vector = @splat(0);
515 try std.testing.expect(@reduce(.And, zipLower(D, value, value) ==
516 @as(D.repartition(wideInteger(T)).Vector, @splat(0))));
517 try std.testing.expect(@reduce(.And, zipUpper(D, value, value) ==
518 @as(D.repartition(wideInteger(T)).Vector, @splat(0))));
519 }
520 }