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 }