lib/simd/src/slide.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub fn slideUpLanes(comptime D: type, value: D.Vector, amount: usize) D.Vector {
4 std.debug.assert(amount <= D.lane_count);
5 const lanes: [D.lane_count]D.Lane = @bitCast(value);
6 var result = @as([D.lane_count]D.Lane, @splat(0));
7 for (amount..D.lane_count) |index| result[index] = lanes[index - amount];
8 return @bitCast(result);
9 }
10
11 pub fn slideDownLanes(comptime D: type, value: D.Vector, amount: usize) D.Vector {
12 std.debug.assert(amount <= D.lane_count);
13 const lanes: [D.lane_count]D.Lane = @bitCast(value);
14 var result = @as([D.lane_count]D.Lane, @splat(0));
15 for (0..D.lane_count - amount) |index| result[index] = lanes[index + amount];
16 return @bitCast(result);
17 }
18
19 pub fn slideUpLanesOr(
20 comptime D: type,
21 low: D.Vector,
22 high: D.Vector,
23 amount: usize,
24 ) D.Vector {
25 std.debug.assert(amount <= D.lane_count);
26 const low_lanes: [D.lane_count]D.Lane = @bitCast(low);
27 const high_lanes: [D.lane_count]D.Lane = @bitCast(high);
28 var result: [D.lane_count]D.Lane = undefined;
29 for (0..D.lane_count) |index| {
30 result[index] = if (index < amount) low_lanes[index] else high_lanes[index - amount];
31 }
32 return @bitCast(result);
33 }
34
35 pub fn slideDownLanesOr(
36 comptime D: type,
37 high: D.Vector,
38 low: D.Vector,
39 amount: usize,
40 ) D.Vector {
41 std.debug.assert(amount <= D.lane_count);
42 const high_lanes: [D.lane_count]D.Lane = @bitCast(high);
43 const low_lanes: [D.lane_count]D.Lane = @bitCast(low);
44 var result: [D.lane_count]D.Lane = undefined;
45 for (0..D.lane_count) |index| {
46 result[index] = if (index < D.lane_count - amount)
47 low_lanes[index + amount]
48 else
49 high_lanes[index];
50 }
51 return @bitCast(result);
52 }
53
54 pub fn slide1Up(comptime D: type, value: D.Vector) D.Vector {
55 return slideUpLanes(D, value, 1);
56 }
57
58 pub fn slide1Down(comptime D: type, value: D.Vector) D.Vector {
59 return slideDownLanes(D, value, 1);
60 }
61
62 pub fn slide1UpOr(comptime D: type, fill: D.Lane, value: D.Vector) D.Vector {
63 var result = slide1Up(D, value);
64 result[0] = fill;
65 return result;
66 }
67
68 pub fn slide1DownOr(comptime D: type, fill: D.Lane, value: D.Vector) D.Vector {
69 var result = slide1Down(D, value);
70 result[D.lane_count - 1] = fill;
71 return result;
72 }
73
74 pub fn slideUpBlocks(comptime D: type, comptime blocks: usize, value: D.Vector) D.Vector {
75 const lanes_per_block = 16 / @sizeOf(D.Lane);
76 if (blocks * lanes_per_block > D.lane_count) @compileError("block slide exceeds the vector");
77 return slideUpLanes(D, value, blocks * lanes_per_block);
78 }
79
80 pub fn slideDownBlocks(comptime D: type, comptime blocks: usize, value: D.Vector) D.Vector {
81 const lanes_per_block = 16 / @sizeOf(D.Lane);
82 if (blocks * lanes_per_block > D.lane_count) @compileError("block slide exceeds the vector");
83 return slideDownLanes(D, value, blocks * lanes_per_block);
84 }
85
86 pub fn slideMaskUpLanes(comptime D: type, mask: D.Mask, amount: usize) D.Mask {
87 std.debug.assert(amount <= D.lane_count);
88 var lanes: [D.lane_count]bool = undefined;
89 inline for (0..D.lane_count) |index| lanes[index] = mask[index];
90 var shifted = @as([D.lane_count]bool, @splat(false));
91 for (amount..D.lane_count) |index| shifted[index] = lanes[index - amount];
92 var result: D.Mask = undefined;
93 inline for (0..D.lane_count) |index| result[index] = shifted[index];
94 return result;
95 }
96
97 pub fn slideMaskDownLanes(comptime D: type, mask: D.Mask, amount: usize) D.Mask {
98 std.debug.assert(amount <= D.lane_count);
99 var lanes: [D.lane_count]bool = undefined;
100 inline for (0..D.lane_count) |index| lanes[index] = mask[index];
101 var shifted = @as([D.lane_count]bool, @splat(false));
102 for (0..D.lane_count - amount) |index| shifted[index] = lanes[index + amount];
103 var result: D.Mask = undefined;
104 inline for (0..D.lane_count) |index| result[index] = shifted[index];
105 return result;
106 }
107
108 pub fn slideMask1Up(comptime D: type, mask: D.Mask) D.Mask {
109 return slideMaskUpLanes(D, mask, 1);
110 }
111
112 pub fn slideMask1Down(comptime D: type, mask: D.Mask) D.Mask {
113 return slideMaskDownLanes(D, mask, 1);
114 }
115
116 fn verifyLaneType(comptime T: type) !void {
117 const simd = @import("root.zig");
118 const D = simd.FixedTag(T, 4);
119 const zero: D.Vector = @splat(0);
120 try std.testing.expect(@reduce(.And, slideUpLanes(D, zero, 2) == zero));
121 try std.testing.expect(@reduce(.And, slideDownLanes(D, zero, 2) == zero));
122 }
123
124 test "Highway slides instantiate every lane type" {
125 inline for (.{ u8, i8, u16, i16, u32, i32, u64, i64, f16, f32, f64 }) |T| {
126 try verifyLaneType(T);
127 }
128 }
129
130 test "Highway lane slides cross blocks and preserve explicit fill lanes" {
131 const simd = @import("root.zig");
132 const D = simd.FixedTag(i32, 8);
133 const value: D.Vector = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
134 const fill: D.Vector = @splat(99);
135 try std.testing.expect(@reduce(.And, slideUpLanes(D, value, 3) == @as(D.Vector, .{ 0, 0, 0, 0, 1, 2, 3, 4 })));
136 try std.testing.expect(@reduce(.And, slideDownLanes(D, value, 3) == @as(D.Vector, .{ 3, 4, 5, 6, 7, 0, 0, 0 })));
137 try std.testing.expect(@reduce(.And, slideUpLanesOr(D, fill, value, 3) == @as(D.Vector, .{ 99, 99, 99, 0, 1, 2, 3, 4 })));
138 try std.testing.expect(@reduce(.And, slideDownLanesOr(D, fill, value, 3) == @as(D.Vector, .{ 3, 4, 5, 6, 7, 99, 99, 99 })));
139 try std.testing.expect(@reduce(.And, slide1UpOr(D, -1, value) == @as(D.Vector, .{ -1, 0, 1, 2, 3, 4, 5, 6 })));
140 try std.testing.expect(@reduce(.And, slide1DownOr(D, -1, value) == @as(D.Vector, .{ 1, 2, 3, 4, 5, 6, 7, -1 })));
141 }
142
143 test "Highway mask and block slides use the same whole-vector geometry" {
144 const simd = @import("root.zig");
145 const D = simd.FixedTag(u32, 8);
146 const mask: D.Mask = .{ true, false, true, false, false, true, false, true };
147 try std.testing.expect(@reduce(.And, slideMaskUpLanes(D, mask, 2) ==
148 @as(D.Mask, .{ false, false, true, false, true, false, false, true })));
149 try std.testing.expect(@reduce(.And, slideMaskDownLanes(D, mask, 2) ==
150 @as(D.Mask, .{ true, false, false, true, false, true, false, false })));
151 const value: D.Vector = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
152 try std.testing.expect(@reduce(.And, slideUpBlocks(D, 1, value) == @as(D.Vector, .{ 0, 0, 0, 0, 0, 1, 2, 3 })));
153 try std.testing.expect(@reduce(.And, slideDownBlocks(D, 1, value) == @as(D.Vector, .{ 4, 5, 6, 7, 0, 0, 0, 0 })));
154 }