#[allow(dead_code)]
pub(crate) trait Simd {
type Elem: Copy;
type Vec: Copy;
const LANES: usize;
const LOG_LANES: u32;
const LSS: i32;
const RSS: i32;
const NEG_INF: Self::Elem;
fn splat(value: Self::Elem) -> Self::Vec;
fn add(a: Self::Vec, b: Self::Vec) -> Self::Vec;
fn sub(a: Self::Vec, b: Self::Vec) -> Self::Vec;
fn min(a: Self::Vec, b: Self::Vec) -> Self::Vec;
fn max(a: Self::Vec, b: Self::Vec) -> Self::Vec;
fn or(a: Self::Vec, b: Self::Vec) -> Self::Vec;
fn loadu(src: &[Self::Elem]) -> Self::Vec;
fn storeu(v: Self::Vec, dst: &mut [Self::Elem]);
fn store_widened_i32(v: Self::Vec, dst: &mut [i32]);
fn slli<const N: i32>(v: Self::Vec) -> Self::Vec;
fn srli<const N: i32>(v: Self::Vec) -> Self::Vec;
fn slli_one_lane(v: Self::Vec) -> Self::Vec;
fn srli_top_lane(v: Self::Vec) -> Self::Vec;
fn horizontal_max(v: Self::Vec) -> Self::Elem;
fn prefix_max(v: Self::Vec, penalties: &[Self::Vec], masks: &[Self::Vec]) -> Self::Vec;
#[inline(always)]
fn prefix_max_step<const N: i32>(
a: Self::Vec,
penalty: Self::Vec,
mask: Self::Vec,
) -> Self::Vec {
Self::max(a, Self::or(mask, Self::slli::<N>(Self::add(a, penalty))))
}
}
#[allow(dead_code)]
const NEG_INF_I16: i16 = i16::MIN + 1024;
#[allow(dead_code)]
const NEG_INF_I32: i32 = i32::MIN + 1024;
#[allow(dead_code)]
pub(crate) struct ScalarSimdI16;
impl Simd for ScalarSimdI16 {
type Elem = i16;
type Vec = i16;
const LANES: usize = 1;
const LOG_LANES: u32 = 0;
const LSS: i32 = size_of::<i16>() as i32;
const RSS: i32 = 0;
const NEG_INF: i16 = NEG_INF_I16;
#[inline(always)]
fn splat(value: i16) -> i16 {
value
}
#[inline(always)]
fn add(a: i16, b: i16) -> i16 {
a.wrapping_add(b)
}
#[inline(always)]
fn sub(a: i16, b: i16) -> i16 {
a.wrapping_sub(b)
}
#[inline(always)]
fn min(a: i16, b: i16) -> i16 {
a.min(b)
}
#[inline(always)]
fn max(a: i16, b: i16) -> i16 {
a.max(b)
}
#[inline(always)]
fn or(a: i16, b: i16) -> i16 {
a | b
}
#[inline(always)]
fn loadu(src: &[i16]) -> i16 {
src[0]
}
#[inline(always)]
fn storeu(v: i16, dst: &mut [i16]) {
dst[0] = v;
}
#[inline(always)]
fn store_widened_i32(v: i16, dst: &mut [i32]) {
dst[0] = i32::from(v);
}
#[inline(always)]
fn slli<const N: i32>(_v: i16) -> i16 {
Self::NEG_INF
}
#[inline(always)]
fn srli<const N: i32>(_v: i16) -> i16 {
Self::NEG_INF
}
#[inline(always)]
fn slli_one_lane(_v: i16) -> i16 {
Self::NEG_INF
}
#[inline(always)]
fn srli_top_lane(_v: i16) -> i16 {
Self::NEG_INF
}
#[inline(always)]
fn horizontal_max(v: i16) -> i16 {
0i16.max(v)
}
#[inline(always)]
fn prefix_max(v: i16, _penalties: &[i16], _masks: &[i16]) -> i16 {
v
}
}
#[allow(dead_code)]
pub(crate) struct ScalarSimdI32;
impl Simd for ScalarSimdI32 {
type Elem = i32;
type Vec = i32;
const LANES: usize = 1;
const LOG_LANES: u32 = 0;
const LSS: i32 = size_of::<i32>() as i32;
const RSS: i32 = 0;
const NEG_INF: i32 = NEG_INF_I32;
#[inline(always)]
fn splat(value: i32) -> i32 {
value
}
#[inline(always)]
fn add(a: i32, b: i32) -> i32 {
a.wrapping_add(b)
}
#[inline(always)]
fn sub(a: i32, b: i32) -> i32 {
a.wrapping_sub(b)
}
#[inline(always)]
fn min(a: i32, b: i32) -> i32 {
a.min(b)
}
#[inline(always)]
fn max(a: i32, b: i32) -> i32 {
a.max(b)
}
#[inline(always)]
fn or(a: i32, b: i32) -> i32 {
a | b
}
#[inline(always)]
fn loadu(src: &[i32]) -> i32 {
src[0]
}
#[inline(always)]
fn storeu(v: i32, dst: &mut [i32]) {
dst[0] = v;
}
#[inline(always)]
fn store_widened_i32(v: i32, dst: &mut [i32]) {
dst[0] = v;
}
#[inline(always)]
fn slli<const N: i32>(_v: i32) -> i32 {
Self::NEG_INF
}
#[inline(always)]
fn srli<const N: i32>(_v: i32) -> i32 {
Self::NEG_INF
}
#[inline(always)]
fn slli_one_lane(_v: i32) -> i32 {
Self::NEG_INF
}
#[inline(always)]
fn srli_top_lane(_v: i32) -> i32 {
Self::NEG_INF
}
#[inline(always)]
fn horizontal_max(v: i32) -> i32 {
0i32.max(v)
}
#[inline(always)]
fn prefix_max(v: i32, _penalties: &[i32], _masks: &[i32]) -> i32 {
v
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn neg_inf_matches_elem_min_plus_1024() {
assert_eq!(ScalarSimdI16::NEG_INF, i16::MIN + 1024);
assert_eq!(ScalarSimdI32::NEG_INF, i32::MIN + 1024);
}
#[test]
fn i16_splat_is_identity() {
assert_eq!(ScalarSimdI16::splat(7), 7);
assert_eq!(ScalarSimdI16::splat(-3), -3);
}
#[test]
fn i16_arithmetic_matches_plain_integer_ops() {
assert_eq!(ScalarSimdI16::add(3, 4), 7);
assert_eq!(ScalarSimdI16::sub(10, 4), 6);
assert_eq!(ScalarSimdI16::min(10, 4), 4);
assert_eq!(ScalarSimdI16::max(10, 4), 10);
assert_eq!(ScalarSimdI16::or(0b0101, 0b1010), 0b1111);
}
#[test]
fn i16_add_sub_are_non_saturating() {
assert_eq!(ScalarSimdI16::add(i16::MAX, 1), i16::MIN);
assert_eq!(ScalarSimdI16::sub(i16::MIN, 1), i16::MAX);
}
#[test]
fn i16_horizontal_max_seeds_at_zero_not_elem_min() {
assert_eq!(ScalarSimdI16::horizontal_max(-5), 0);
assert_eq!(ScalarSimdI16::horizontal_max(7), 7);
assert_eq!(ScalarSimdI16::horizontal_max(0), 0);
}
#[test]
fn i16_prefix_max_of_one_lane_is_identity() {
assert_eq!(ScalarSimdI16::prefix_max(42, &[], &[]), 42);
assert_eq!(ScalarSimdI16::prefix_max(-9, &[], &[]), -9);
}
#[test]
fn i16_loadu_storeu_round_trip() {
let src = [11i16];
let v = ScalarSimdI16::loadu(&src);
let mut dst = [0i16];
ScalarSimdI16::storeu(v, &mut dst);
assert_eq!(dst, src);
}
#[test]
fn i16_slli_srli_of_one_lane_return_neg_inf() {
assert_eq!(ScalarSimdI16::slli::<2>(5), ScalarSimdI16::NEG_INF);
assert_eq!(ScalarSimdI16::srli::<2>(5), ScalarSimdI16::NEG_INF);
}
#[test]
fn i32_splat_is_identity() {
assert_eq!(ScalarSimdI32::splat(7), 7);
assert_eq!(ScalarSimdI32::splat(-3), -3);
}
#[test]
fn i32_arithmetic_matches_plain_integer_ops() {
assert_eq!(ScalarSimdI32::add(3, 4), 7);
assert_eq!(ScalarSimdI32::sub(10, 4), 6);
assert_eq!(ScalarSimdI32::min(10, 4), 4);
assert_eq!(ScalarSimdI32::max(10, 4), 10);
assert_eq!(ScalarSimdI32::or(0b0101, 0b1010), 0b1111);
}
#[test]
fn i32_add_sub_are_non_saturating() {
assert_eq!(ScalarSimdI32::add(i32::MAX, 1), i32::MIN);
assert_eq!(ScalarSimdI32::sub(i32::MIN, 1), i32::MAX);
}
#[test]
fn i32_horizontal_max_seeds_at_zero_not_elem_min() {
assert_eq!(ScalarSimdI32::horizontal_max(-5), 0);
assert_eq!(ScalarSimdI32::horizontal_max(7), 7);
assert_eq!(ScalarSimdI32::horizontal_max(0), 0);
}
#[test]
fn i32_prefix_max_of_one_lane_is_identity() {
assert_eq!(ScalarSimdI32::prefix_max(42, &[], &[]), 42);
assert_eq!(ScalarSimdI32::prefix_max(-9, &[], &[]), -9);
}
#[test]
fn i32_loadu_storeu_round_trip() {
let src = [11i32];
let v = ScalarSimdI32::loadu(&src);
let mut dst = [0i32];
ScalarSimdI32::storeu(v, &mut dst);
assert_eq!(dst, src);
}
#[test]
fn i32_slli_srli_of_one_lane_return_neg_inf() {
assert_eq!(ScalarSimdI32::slli::<4>(5), ScalarSimdI32::NEG_INF);
assert_eq!(ScalarSimdI32::srli::<4>(5), ScalarSimdI32::NEG_INF);
}
#[test]
fn lanes_and_log_lanes_are_degenerate() {
assert_eq!(ScalarSimdI16::LANES, 1);
assert_eq!(ScalarSimdI16::LOG_LANES, 0);
assert_eq!(ScalarSimdI32::LANES, 1);
assert_eq!(ScalarSimdI32::LOG_LANES, 0);
}
}