use himada_core::HardwareDNA;
pub fn dot_i32_scalar(a: &[i32], b: &[i32]) -> i32 {
let len = a.len().min(b.len());
let mut sum = 0i32;
for i in 0..len {
sum = sum.wrapping_add(a[i].wrapping_mul(b[i]));
}
sum
}
pub fn dot_i32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn dot_i32_neon(a: &[i32], b: &[i32]) -> i32 {
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc = unsafe { std::arch::aarch64::vdupq_n_s32(0) };
unsafe {
use std::arch::aarch64::*;
while i + 4 <= len {
let va = vld1q_s32(a.as_ptr().add(i));
let vb = vld1q_s32(b.as_ptr().add(i));
vacc = vaddq_s32(vacc, vmulq_s32(va, vb));
i += 4;
}
}
let mut sum = unsafe { std::arch::aarch64::vaddvq_s32(vacc) };
for j in i..len {
sum = sum.wrapping_add(a[j].wrapping_mul(b[j]));
}
sum
}
#[cfg(target_arch = "aarch64")]
pub fn dot_i32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn dot_i32_neon(_: &[i32], _: &[i32]) -> i32 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn dot_i32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn dot_i32_sse(a: &[i32], b: &[i32]) -> i32 {
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm_setzero_si128() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse4.1") {
while i + 4 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
vacc = _mm_add_epi32(vacc, _mm_mullo_epi32(va, vb));
i += 4;
}
}
}
let tmp: [i32; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp[0].wrapping_add(tmp[1]).wrapping_add(tmp[2]).wrapping_add(tmp[3]);
for j in i..len {
sum = sum.wrapping_add(a[j].wrapping_mul(b[j]));
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn dot_i32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i32_sse(_: &[i32], _: &[i32]) -> i32 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn dot_i32_avx2(a: &[i32], b: &[i32]) -> i32 {
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm256_setzero_si256() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 8 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
vacc = _mm256_add_epi32(vacc, _mm256_mullo_epi32(va, vb));
i += 8;
}
}
}
let tmp: [i32; 8] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp.iter().fold(0i32, |a, b| a.wrapping_add(*b));
for j in i..len {
sum = sum.wrapping_add(a[j].wrapping_mul(b[j]));
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn dot_i32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i32_avx2(_: &[i32], _: &[i32]) -> i32 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn dot_i64_scalar(a: &[i64], b: &[i64]) -> i64 {
let len = a.len().min(b.len());
let mut sum = 0i64;
for i in 0..len {
sum = sum.wrapping_add(a[i].wrapping_mul(b[i]));
}
sum
}
pub fn dot_i64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn dot_i64_neon(a: &[i64], b: &[i64]) -> i64 {
let len = a.len().min(b.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
let mut vacc = vdupq_n_s64(0);
while i + 2 <= len {
let p0 = a[i].wrapping_mul(b[i]);
let p1 = a[i + 1].wrapping_mul(b[i + 1]);
let vp: int64x2_t = std::mem::transmute([p0, p1]);
vacc = vaddq_s64(vacc, vp);
i += 2;
}
let mut sum = vaddvq_s64(vacc);
for j in i..len {
sum = sum.wrapping_add(a[j].wrapping_mul(b[j]));
}
sum
}
}
#[cfg(target_arch = "aarch64")]
pub fn dot_i64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn dot_i64_neon(_: &[i64], _: &[i64]) -> i64 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn dot_i64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn dot_i64_sse(a: &[i64], b: &[i64]) -> i64 {
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm_setzero_si128() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 2 <= len {
let p0 = a[i].wrapping_mul(b[i]);
let p1 = a[i + 1].wrapping_mul(b[i + 1]);
let vp = _mm_set_epi64x(p1, p0);
vacc = _mm_add_epi64(vacc, vp);
i += 2;
}
}
}
let tmp: [i64; 2] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp[0].wrapping_add(tmp[1]);
for j in i..len {
sum = sum.wrapping_add(a[j].wrapping_mul(b[j]));
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn dot_i64_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i64_sse(_: &[i64], _: &[i64]) -> i64 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn dot_i64_avx2(a: &[i64], b: &[i64]) -> i64 {
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm256_setzero_si256() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 4 <= len {
let p0 = a[i].wrapping_mul(b[i]);
let p1 = a[i + 1].wrapping_mul(b[i + 1]);
let p2 = a[i + 2].wrapping_mul(b[i + 2]);
let p3 = a[i + 3].wrapping_mul(b[i + 3]);
let vp = _mm256_set_epi64x(p3, p2, p1, p0);
vacc = _mm256_add_epi64(vacc, vp);
i += 4;
}
}
}
let tmp: [i64; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp.iter().fold(0i64, |a, b| a.wrapping_add(*b));
for j in i..len {
sum = sum.wrapping_add(a[j].wrapping_mul(b[j]));
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn dot_i64_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i64_avx2(_: &[i64], _: &[i64]) -> i64 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn dot_i16_scalar(a: &[i16], b: &[i16]) -> i16 {
let len = a.len().min(b.len());
let mut sum = 0i16;
for i in 0..len {
sum = sum.wrapping_add(a[i].wrapping_mul(b[i]));
}
sum
}
pub fn dot_i16_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn dot_i16_neon(a: &[i16], b: &[i16]) -> i16 {
let len = a.len().min(b.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
let mut vacc = vdupq_n_s16(0);
while i + 8 <= len {
let va = vld1q_s16(a.as_ptr().add(i));
let vb = vld1q_s16(b.as_ptr().add(i));
vacc = vaddq_s16(vacc, vmulq_s16(va, vb));
i += 8;
}
let mut sum = vaddvq_s16(vacc);
for j in i..len {
sum = sum.wrapping_add(a[j].wrapping_mul(b[j]));
}
sum
}
}
#[cfg(target_arch = "aarch64")]
pub fn dot_i16_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn dot_i16_neon(_: &[i16], _: &[i16]) -> i16 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn dot_i16_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn dot_i16_sse(a: &[i16], b: &[i16]) -> i16 {
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm_setzero_si128() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 8 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
vacc = _mm_add_epi16(vacc, _mm_mullo_epi16(va, vb));
i += 8;
}
}
}
let tmp: [i16; 8] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp.iter().fold(0i16, |a, b| a.wrapping_add(*b));
for j in i..len {
sum = sum.wrapping_add(a[j].wrapping_mul(b[j]));
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn dot_i16_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i16_sse(_: &[i16], _: &[i16]) -> i16 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i16_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn dot_i16_avx2(a: &[i16], b: &[i16]) -> i16 {
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm256_setzero_si256() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 16 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
vacc = _mm256_add_epi16(vacc, _mm256_mullo_epi16(va, vb));
i += 16;
}
}
}
let tmp: [i16; 16] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp.iter().fold(0i16, |a, b| a.wrapping_add(*b));
for j in i..len {
sum = sum.wrapping_add(a[j].wrapping_mul(b[j]));
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn dot_i16_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i16_avx2(_: &[i16], _: &[i16]) -> i16 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i16_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn dot_i8_scalar(a: &[i8], b: &[i8]) -> i8 {
let len = a.len().min(b.len());
let mut sum = 0i8;
for i in 0..len {
sum = sum.wrapping_add(a[i].wrapping_mul(b[i]));
}
sum
}
pub fn dot_i8_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn dot_i8_neon(a: &[i8], b: &[i8]) -> i8 {
let len = a.len().min(b.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
let mut vacc = vdupq_n_s8(0);
while i + 16 <= len {
let va = vld1q_s8(a.as_ptr().add(i));
let vb = vld1q_s8(b.as_ptr().add(i));
vacc = vaddq_s8(vacc, vmulq_s8(va, vb));
i += 16;
}
let mut sum = vaddvq_s8(vacc);
for j in i..len {
sum = sum.wrapping_add(a[j].wrapping_mul(b[j]));
}
sum
}
}
#[cfg(target_arch = "aarch64")]
pub fn dot_i8_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn dot_i8_neon(_: &[i8], _: &[i8]) -> i8 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn dot_i8_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn dot_i8_sse(a: &[i8], b: &[i8]) -> i8 {
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc_wide = unsafe { std::arch::x86_64::_mm_setzero_si128() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse4.1") {
while i + 16 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
let va_lo = _mm_cvtepi8_epi16(va);
let va_hi = _mm_cvtepi8_epi16(_mm_srli_si128(va, 8));
let vb_lo = _mm_cvtepi8_epi16(vb);
let vb_hi = _mm_cvtepi8_epi16(_mm_srli_si128(vb, 8));
let prod_lo = _mm_mullo_epi16(va_lo, vb_lo);
let prod_hi = _mm_mullo_epi16(va_hi, vb_hi);
vacc_wide = _mm_add_epi16(vacc_wide, _mm_add_epi16(prod_lo, prod_hi));
i += 16;
}
}
}
let tmp: [i16; 8] = unsafe { std::mem::transmute(vacc_wide) };
let mut sum: i16 = tmp.iter().fold(0i16, |a, b| a.wrapping_add(*b));
for j in i..len {
sum = sum.wrapping_add(a[j] as i16 * b[j] as i16);
}
sum as i8
}
#[cfg(target_arch = "x86_64")]
pub fn dot_i8_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i8_sse(_: &[i8], _: &[i8]) -> i8 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i8_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn dot_i8_avx2(a: &[i8], b: &[i8]) -> i8 {
let len = a.len().min(b.len());
let mut i = 0;
let mut vacc_wide = unsafe { std::arch::x86_64::_mm256_setzero_si256() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 32 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
let va_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(va));
let va_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(va, 1));
let vb_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(vb));
let vb_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(vb, 1));
let prod_lo = _mm256_mullo_epi16(va_lo, vb_lo);
let prod_hi = _mm256_mullo_epi16(va_hi, vb_hi);
vacc_wide = _mm256_add_epi16(vacc_wide, _mm256_add_epi16(prod_lo, prod_hi));
i += 32;
}
}
}
let tmp: [i16; 16] = unsafe { std::mem::transmute(vacc_wide) };
let mut sum: i16 = tmp.iter().fold(0i16, |a, b| a.wrapping_add(*b));
for j in i..len {
sum = sum.wrapping_add(a[j] as i16 * b[j] as i16);
}
sum as i8
}
#[cfg(target_arch = "x86_64")]
pub fn dot_i8_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i8_avx2(_: &[i8], _: &[i8]) -> i8 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn dot_i8_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_sum_i32_scalar(a: &[i32]) -> i32 {
let mut sum = 0i32;
for &v in a {
sum = sum.wrapping_add(v);
}
sum
}
pub fn reduce_sum_i32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_i32_neon(a: &[i32]) -> i32 {
let mut i = 0;
let mut vacc = unsafe { std::arch::aarch64::vdupq_n_s32(0) };
unsafe {
use std::arch::aarch64::*;
while i + 4 <= a.len() {
let v = vld1q_s32(a.as_ptr().add(i));
vacc = vaddq_s32(vacc, v);
i += 4;
}
}
let mut sum = unsafe { std::arch::aarch64::vaddvq_s32(vacc) };
for &v in &a[i..] {
sum = sum.wrapping_add(v);
}
sum
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_i32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_i32_neon(_: &[i32]) -> i32 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_i32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i32_sse(a: &[i32]) -> i32 {
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm_setzero_si128() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 4 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
vacc = _mm_add_epi32(vacc, v);
i += 4;
}
}
}
let tmp: [i32; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp[0].wrapping_add(tmp[1]).wrapping_add(tmp[2]).wrapping_add(tmp[3]);
for &v in &a[i..] {
sum = sum.wrapping_add(v);
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i32_sse(_: &[i32]) -> i32 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i32_avx2(a: &[i32]) -> i32 {
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm256_setzero_si256() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 8 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
vacc = _mm256_add_epi32(vacc, v);
i += 8;
}
}
}
let tmp: [i32; 8] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp.iter().fold(0i32, |a, b| a.wrapping_add(*b));
for &v in &a[i..] {
sum = sum.wrapping_add(v);
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i32_avx2(_: &[i32]) -> i32 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_sum_i64_scalar(a: &[i64]) -> i64 {
let mut sum = 0i64;
for &v in a {
sum = sum.wrapping_add(v);
}
sum
}
pub fn reduce_sum_i64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_i64_neon(a: &[i64]) -> i64 {
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
let mut vacc = vdupq_n_s64(0);
while i + 2 <= a.len() {
let v = vld1q_s64(a.as_ptr().add(i));
vacc = vaddq_s64(vacc, v);
i += 2;
}
let mut sum = vaddvq_s64(vacc);
for &v in &a[i..] {
sum = sum.wrapping_add(v);
}
sum
}
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_i64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_i64_neon(_: &[i64]) -> i64 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_i64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i64_sse(a: &[i64]) -> i64 {
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm_setzero_si128() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 2 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
vacc = _mm_add_epi64(vacc, v);
i += 2;
}
}
}
let tmp: [i64; 2] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp[0].wrapping_add(tmp[1]);
for &v in &a[i..] {
sum = sum.wrapping_add(v);
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i64_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i64_sse(_: &[i64]) -> i64 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i64_avx2(a: &[i64]) -> i64 {
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm256_setzero_si256() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 4 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
vacc = _mm256_add_epi64(vacc, v);
i += 4;
}
}
}
let tmp: [i64; 4] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp.iter().fold(0i64, |a, b| a.wrapping_add(*b));
for &v in &a[i..] {
sum = sum.wrapping_add(v);
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i64_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i64_avx2(_: &[i64]) -> i64 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_sum_i16_scalar(a: &[i16]) -> i16 {
let mut sum = 0i16;
for &v in a {
sum = sum.wrapping_add(v);
}
sum
}
pub fn reduce_sum_i16_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_i16_neon(a: &[i16]) -> i16 {
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
let mut vacc = vdupq_n_s16(0);
while i + 8 <= a.len() {
let v = vld1q_s16(a.as_ptr().add(i));
vacc = vaddq_s16(vacc, v);
i += 8;
}
let mut sum = vaddvq_s16(vacc);
for &v in &a[i..] {
sum = sum.wrapping_add(v);
}
sum
}
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_i16_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_i16_neon(_: &[i16]) -> i16 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_i16_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i16_sse(a: &[i16]) -> i16 {
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm_setzero_si128() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 8 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
vacc = _mm_add_epi16(vacc, v);
i += 8;
}
}
}
let tmp: [i16; 8] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp.iter().fold(0i16, |a, b| a.wrapping_add(*b));
for &v in &a[i..] {
sum = sum.wrapping_add(v);
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i16_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i16_sse(_: &[i16]) -> i16 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i16_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i16_avx2(a: &[i16]) -> i16 {
let mut i = 0;
let mut vacc = unsafe { std::arch::x86_64::_mm256_setzero_si256() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 16 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
vacc = _mm256_add_epi16(vacc, v);
i += 16;
}
}
}
let tmp: [i16; 16] = unsafe { std::mem::transmute(vacc) };
let mut sum = tmp.iter().fold(0i16, |a, b| a.wrapping_add(*b));
for &v in &a[i..] {
sum = sum.wrapping_add(v);
}
sum
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i16_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i16_avx2(_: &[i16]) -> i16 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i16_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_sum_i8_scalar(a: &[i8]) -> i8 {
let mut sum = 0i8;
for &v in a {
sum = sum.wrapping_add(v);
}
sum
}
pub fn reduce_sum_i8_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_i8_neon(a: &[i8]) -> i8 {
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
let mut vacc = vdupq_n_s8(0);
while i + 16 <= a.len() {
let v = vld1q_s8(a.as_ptr().add(i));
vacc = vaddq_s8(vacc, v);
i += 16;
}
let mut sum = vaddvq_s8(vacc);
for &v in &a[i..] {
sum = sum.wrapping_add(v);
}
sum
}
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_sum_i8_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_i8_neon(_: &[i8]) -> i8 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_sum_i8_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i8_sse(a: &[i8]) -> i8 {
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
let mut vacc = _mm_setzero_si128();
if is_x86_feature_detected!("sse2") {
while i + 16 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
vacc = _mm_add_epi8(vacc, v);
i += 16;
}
}
let tmp: [i8; 16] = std::mem::transmute(vacc);
let mut sum: i16 = 0;
for &v in &tmp {
sum = sum.wrapping_add(v as i16);
}
for &v in &a[i..] {
sum = sum.wrapping_add(v as i16);
}
sum as i8
}
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i8_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i8_sse(_: &[i8]) -> i8 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i8_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i8_avx2(a: &[i8]) -> i8 {
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
let mut vacc = _mm256_setzero_si256();
if is_x86_feature_detected!("avx2") {
while i + 32 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
vacc = _mm256_add_epi8(vacc, v);
i += 32;
}
}
let tmp: [i8; 32] = std::mem::transmute(vacc);
let mut sum: i16 = 0;
for &v in &tmp {
sum = sum.wrapping_add(v as i16);
}
for &v in &a[i..] {
sum = sum.wrapping_add(v as i16);
}
sum as i8
}
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_sum_i8_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i8_avx2(_: &[i8]) -> i8 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_sum_i8_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_max_i32_scalar(a: &[i32]) -> i32 {
let mut max = i32::MIN;
for &v in a {
if v > max {
max = v;
}
}
max
}
pub fn reduce_max_i32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_i32_neon(a: &[i32]) -> i32 {
let mut i = 0;
let mut vmax = unsafe { std::arch::aarch64::vdupq_n_s32(i32::MIN) };
unsafe {
use std::arch::aarch64::*;
while i + 4 <= a.len() {
let v = vld1q_s32(a.as_ptr().add(i));
vmax = vmaxq_s32(vmax, v);
i += 4;
}
}
let tmp: [i32; 4] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp[0].max(tmp[1]).max(tmp[2]).max(tmp[3]);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_i32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_i32_neon(_: &[i32]) -> i32 { i32::MIN }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_i32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i32_sse(a: &[i32]) -> i32 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm_set1_epi32(i32::MIN) };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse4.1") {
while i + 4 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
vmax = _mm_max_epi32(vmax, v);
i += 4;
}
}
}
let tmp: [i32; 4] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp[0].max(tmp[1]).max(tmp[2]).max(tmp[3]);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i32_sse(_: &[i32]) -> i32 { i32::MIN }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i32_avx2(a: &[i32]) -> i32 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm256_set1_epi32(i32::MIN) };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 8 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
vmax = _mm256_max_epi32(vmax, v);
i += 8;
}
}
}
let tmp: [i32; 8] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(i32::MIN, i32::max);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i32_avx2(_: &[i32]) -> i32 { i32::MIN }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_max_i64_scalar(a: &[i64]) -> i64 {
let mut max = i64::MIN;
for &v in a {
if v > max {
max = v;
}
}
max
}
pub fn reduce_max_i64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_i64_neon(a: &[i64]) -> i64 {
let mut i = 0;
let mut max = i64::MIN;
unsafe {
use std::arch::aarch64::*;
while i + 2 <= a.len() {
let v = vld1q_s64(a.as_ptr().add(i));
let arr: [i64; 2] = std::mem::transmute(v);
max = max.max(arr[0]).max(arr[1]);
i += 2;
}
}
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_i64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_i64_neon(_: &[i64]) -> i64 { i64::MIN }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_i64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i64_sse(a: &[i64]) -> i64 {
let mut max = i64::MIN;
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
if is_x86_feature_detected!("sse2") {
while i + 2 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let arr: [i64; 2] = std::mem::transmute(v);
if arr[0] > max { max = arr[0]; }
if arr[1] > max { max = arr[1]; }
i += 2;
}
}
for &v in &a[i..] {
if v > max { max = v; }
}
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i64_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i64_sse(_: &[i64]) -> i64 { i64::MIN }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i64_avx2(a: &[i64]) -> i64 {
let mut max = i64::MIN;
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
if is_x86_feature_detected!("avx2") {
while i + 4 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let arr: [i64; 4] = std::mem::transmute(v);
for k in 0..4 {
if arr[k] > max { max = arr[k]; }
}
i += 4;
}
}
for &v in &a[i..] {
if v > max { max = v; }
}
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i64_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i64_avx2(_: &[i64]) -> i64 { i64::MIN }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_max_i16_scalar(a: &[i16]) -> i16 {
let mut max = i16::MIN;
for &v in a {
if v > max {
max = v;
}
}
max
}
pub fn reduce_max_i16_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_i16_neon(a: &[i16]) -> i16 {
let mut i = 0;
let mut vmax = unsafe { std::arch::aarch64::vdupq_n_s16(i16::MIN) };
unsafe {
use std::arch::aarch64::*;
while i + 8 <= a.len() {
let v = vld1q_s16(a.as_ptr().add(i));
vmax = vmaxq_s16(vmax, v);
i += 8;
}
}
let tmp: [i16; 8] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(i16::MIN, i16::max);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_i16_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_i16_neon(_: &[i16]) -> i16 { i16::MIN }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_i16_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i16_sse(a: &[i16]) -> i16 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm_set1_epi16(i16::MIN as i16) };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 8 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
vmax = _mm_max_epi16(vmax, v);
i += 8;
}
}
}
let tmp: [i16; 8] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(i16::MIN, i16::max);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i16_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i16_sse(_: &[i16]) -> i16 { i16::MIN }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i16_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i16_avx2(a: &[i16]) -> i16 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm256_set1_epi16(i16::MIN as i16) };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 16 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
vmax = _mm256_max_epi16(vmax, v);
i += 16;
}
}
}
let tmp: [i16; 16] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(i16::MIN, i16::max);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i16_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i16_avx2(_: &[i16]) -> i16 { i16::MIN }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i16_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn reduce_max_i8_scalar(a: &[i8]) -> i8 {
let mut max = i8::MIN;
for &v in a {
if v > max {
max = v;
}
}
max
}
pub fn reduce_max_i8_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_i8_neon(a: &[i8]) -> i8 {
let mut i = 0;
let mut vmax = unsafe { std::arch::aarch64::vdupq_n_s8(i8::MIN) };
unsafe {
use std::arch::aarch64::*;
while i + 16 <= a.len() {
let v = vld1q_s8(a.as_ptr().add(i));
vmax = vmaxq_s8(vmax, v);
i += 16;
}
}
let tmp: [i8; 16] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(i8::MIN, i8::max);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "aarch64")]
pub fn reduce_max_i8_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_i8_neon(_: &[i8]) -> i8 { i8::MIN }
#[cfg(not(target_arch = "aarch64"))]
pub fn reduce_max_i8_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i8_sse(a: &[i8]) -> i8 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm_set1_epi8(i8::MIN as i8) };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse4.1") {
while i + 16 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
vmax = _mm_max_epi8(vmax, v);
i += 16;
}
}
}
let tmp: [i8; 16] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(i8::MIN, i8::max);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i8_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i8_sse(_: &[i8]) -> i8 { i8::MIN }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i8_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i8_avx2(a: &[i8]) -> i8 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm256_set1_epi8(i8::MIN as i8) };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 32 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
vmax = _mm256_max_epi8(vmax, v);
i += 32;
}
}
}
let tmp: [i8; 32] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(i8::MIN, i8::max);
for &v in &a[i..] {
if v > max { max = v; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn reduce_max_i8_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i8_avx2(_: &[i8]) -> i8 { i8::MIN }
#[cfg(not(target_arch = "x86_64"))]
pub fn reduce_max_i8_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn abs_max_i32_scalar(a: &[i32]) -> i32 {
let mut max = 0i32;
for &v in a {
let av = v.abs();
if av > max {
max = av;
}
}
max
}
pub fn abs_max_i32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn abs_max_i32_neon(a: &[i32]) -> i32 {
let mut i = 0;
let mut vmax = unsafe { std::arch::aarch64::vdupq_n_s32(0) };
unsafe {
use std::arch::aarch64::*;
while i + 4 <= a.len() {
let v = vld1q_s32(a.as_ptr().add(i));
let va = vabsq_s32(v);
vmax = vmaxq_s32(vmax, va);
i += 4;
}
}
let tmp: [i32; 4] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp[0].max(tmp[1]).max(tmp[2]).max(tmp[3]);
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
max
}
#[cfg(target_arch = "aarch64")]
pub fn abs_max_i32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_i32_neon(_: &[i32]) -> i32 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_i32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i32_sse(a: &[i32]) -> i32 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm_setzero_si128() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse4.1") {
while i + 4 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let va = _mm_abs_epi32(v);
vmax = _mm_max_epi32(vmax, va);
i += 4;
}
}
}
let tmp: [i32; 4] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp[0].max(tmp[1]).max(tmp[2]).max(tmp[3]);
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i32_sse(_: &[i32]) -> i32 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i32_avx2(a: &[i32]) -> i32 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm256_setzero_si256() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 8 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let va = _mm256_abs_epi32(v);
vmax = _mm256_max_epi32(vmax, va);
i += 8;
}
}
}
let tmp: [i32; 8] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(0i32, i32::max);
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i32_avx2(_: &[i32]) -> i32 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn abs_max_i64_scalar(a: &[i64]) -> i64 {
let mut max = 0i64;
for &v in a {
let av = v.abs();
if av > max {
max = av;
}
}
max
}
pub fn abs_max_i64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn abs_max_i64_neon(a: &[i64]) -> i64 {
let mut i = 0;
let mut max = 0;
unsafe {
use std::arch::aarch64::*;
while i + 2 <= a.len() {
let v = vld1q_s64(a.as_ptr().add(i));
let arr: [i64; 2] = std::mem::transmute(v);
max = max.max(arr[0].abs()).max(arr[1].abs());
i += 2;
}
}
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
max
}
#[cfg(target_arch = "aarch64")]
pub fn abs_max_i64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_i64_neon(_: &[i64]) -> i64 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_i64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i64_sse(a: &[i64]) -> i64 {
let mut max = 0i64;
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
if is_x86_feature_detected!("sse2") {
while i + 2 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let arr: [i64; 2] = std::mem::transmute(v);
let a0 = arr[0].abs();
let a1 = arr[1].abs();
if a0 > max { max = a0; }
if a1 > max { max = a1; }
i += 2;
}
}
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i64_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i64_sse(_: &[i64]) -> i64 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i64_avx2(a: &[i64]) -> i64 {
let mut max = 0i64;
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
if is_x86_feature_detected!("avx2") {
while i + 4 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let arr: [i64; 4] = std::mem::transmute(v);
for k in 0..4 {
let av = arr[k].abs();
if av > max { max = av; }
}
i += 4;
}
}
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i64_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i64_avx2(_: &[i64]) -> i64 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn abs_max_i16_scalar(a: &[i16]) -> i16 {
let mut max = 0i16;
for &v in a {
let av = v.abs();
if av > max {
max = av;
}
}
max
}
pub fn abs_max_i16_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn abs_max_i16_neon(a: &[i16]) -> i16 {
let mut i = 0;
let mut vmax = unsafe { std::arch::aarch64::vdupq_n_s16(0) };
unsafe {
use std::arch::aarch64::*;
while i + 8 <= a.len() {
let v = vld1q_s16(a.as_ptr().add(i));
let va = vabsq_s16(v);
vmax = vmaxq_s16(vmax, va);
i += 8;
}
}
let tmp: [i16; 8] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(0i16, i16::max);
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
max
}
#[cfg(target_arch = "aarch64")]
pub fn abs_max_i16_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_i16_neon(_: &[i16]) -> i16 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_i16_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i16_sse(a: &[i16]) -> i16 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm_setzero_si128() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("ssse3") {
while i + 8 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let va = _mm_abs_epi16(v);
vmax = _mm_max_epi16(vmax, va);
i += 8;
}
}
}
let tmp: [i16; 8] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(0i16, i16::max);
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i16_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSSE3" || f == "ssse3")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i16_sse(_: &[i16]) -> i16 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i16_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i16_avx2(a: &[i16]) -> i16 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm256_setzero_si256() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 16 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let va = _mm256_abs_epi16(v);
vmax = _mm256_max_epi16(vmax, va);
i += 16;
}
}
}
let tmp: [i16; 16] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(0i16, i16::max);
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i16_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i16_avx2(_: &[i16]) -> i16 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i16_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn abs_max_i8_scalar(a: &[i8]) -> i8 {
let mut max = 0i8;
for &v in a {
let av = v.abs();
if av > max {
max = av;
}
}
max
}
pub fn abs_max_i8_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn abs_max_i8_neon(a: &[i8]) -> i8 {
let mut i = 0;
let mut vmax = unsafe { std::arch::aarch64::vdupq_n_s8(0) };
unsafe {
use std::arch::aarch64::*;
while i + 16 <= a.len() {
let v = vld1q_s8(a.as_ptr().add(i));
let va = vabsq_s8(v);
vmax = vmaxq_s8(vmax, va);
i += 16;
}
}
let tmp: [i8; 16] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(0i8, i8::max);
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
max
}
#[cfg(target_arch = "aarch64")]
pub fn abs_max_i8_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_i8_neon(_: &[i8]) -> i8 { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn abs_max_i8_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i8_sse(a: &[i8]) -> i8 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm_setzero_si128() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse4.1") {
while i + 16 <= a.len() {
let v = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let va = _mm_abs_epi8(v);
vmax = _mm_max_epi8(vmax, va);
i += 16;
}
}
}
let tmp: [i8; 16] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(0i8, i8::max);
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i8_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i8_sse(_: &[i8]) -> i8 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i8_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i8_avx2(a: &[i8]) -> i8 {
let mut i = 0;
let mut vmax = unsafe { std::arch::x86_64::_mm256_setzero_si256() };
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 32 <= a.len() {
let v = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let va = _mm256_abs_epi8(v);
vmax = _mm256_max_epi8(vmax, va);
i += 32;
}
}
}
let tmp: [i8; 32] = unsafe { std::mem::transmute(vmax) };
let mut max = tmp.iter().copied().fold(0i8, i8::max);
for &v in &a[i..] {
let av = v.abs();
if av > max { max = av; }
}
max
}
#[cfg(target_arch = "x86_64")]
pub fn abs_max_i8_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i8_avx2(_: &[i8]) -> i8 { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn abs_max_i8_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn argmax_i32_scalar(a: &[i32]) -> usize {
let mut idx = 0;
let mut max = i32::MIN;
for (i, &v) in a.iter().enumerate() {
if v > max {
max = v;
idx = i;
}
}
idx
}
pub fn argmax_i32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn argmax_i32_neon(a: &[i32]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::aarch64::*;
let mut i = 0;
let mut vmax = vld1q_s32(a.as_ptr());
let mut vidx: uint32x4_t = std::mem::transmute::<[u32; 4], _>([0, 1, 2, 3]);
i += 4;
while i + 4 <= n {
let va = vld1q_s32(a.as_ptr().add(i));
let vcmp = vcgtq_s32(va, vmax);
vmax = vmaxq_s32(vmax, va);
let local_idx: uint32x4_t = std::mem::transmute::<[u32; 4], _>([i as u32, (i + 1) as u32, (i + 2) as u32, (i + 3) as u32]);
vidx = vbslq_u32(vcmp, local_idx, vidx);
i += 4;
}
let final_vals: [i32; 4] = std::mem::transmute(vmax);
let final_idxs: [u32; 4] = std::mem::transmute(vidx);
let mut best_idx = final_idxs[0] as usize;
let mut best_val = final_vals[0];
for k in 1..4 {
let idx_k = final_idxs[k] as usize;
if idx_k < n && final_vals[k] > best_val {
best_val = final_vals[k];
best_idx = idx_k;
}
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "aarch64")]
pub fn argmax_i32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_i32_neon(_: &[i32]) -> usize { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_i32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_i32_sse(a: &[i32]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
let mut vmax = _mm_loadu_si128(a.as_ptr() as *const __m128i);
let mut vidx = _mm_set_epi32(3, 2, 1, 0);
i += 4;
while i + 4 <= n {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let cmp = _mm_cmpgt_epi32(va, vmax);
let local_idx = _mm_set_epi32((i + 3) as i32, (i + 2) as i32, (i + 1) as i32, i as i32);
vmax = _mm_max_epi32(vmax, va);
vidx = _mm_blendv_epi8(vidx, local_idx, cmp);
i += 4;
}
let vals: [i32; 4] = std::mem::transmute(vmax);
let idxs: [i32; 4] = std::mem::transmute(vidx);
let mut best_idx = idxs[0] as usize;
let mut best_val = vals[0];
for k in 1..4 {
if vals[k] > best_val { best_val = vals[k]; best_idx = idxs[k] as usize; }
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_i32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i32_sse(_: &[i32]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_i32_avx2(a: &[i32]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
let mut vmax = _mm256_loadu_si256(a.as_ptr() as *const __m256i);
let mut vidx = _mm256_set_epi32(7, 6, 5, 4, 3, 2, 1, 0);
i += 8;
while i + 8 <= n {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let cmp = _mm256_cmpgt_epi32(va, vmax);
let local_idx = _mm256_set_epi32((i + 7) as i32, (i + 6) as i32, (i + 5) as i32, (i + 4) as i32, (i + 3) as i32, (i + 2) as i32, (i + 1) as i32, i as i32);
vmax = _mm256_max_epi32(vmax, va);
vidx = _mm256_blendv_epi8(vidx, local_idx, cmp);
i += 8;
}
let vals: [i32; 8] = std::mem::transmute(vmax);
let idxs: [i32; 8] = std::mem::transmute(vidx);
let mut best_idx = idxs[0] as usize;
let mut best_val = vals[0];
for k in 1..8 {
if vals[k] > best_val { best_val = vals[k]; best_idx = idxs[k] as usize; }
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_i32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i32_avx2(_: &[i32]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn argmax_i64_scalar(a: &[i64]) -> usize {
let mut idx = 0;
let mut max = i64::MIN;
for (i, &v) in a.iter().enumerate() {
if v > max {
max = v;
idx = i;
}
}
idx
}
pub fn argmax_i64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn argmax_i64_neon(a: &[i64]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::aarch64::*;
let mut i = 1;
let mut best_idx = 0usize;
let mut best_val = a[0];
while i + 2 <= n {
let v = vld1q_s64(a.as_ptr().add(i));
let arr: [i64; 2] = std::mem::transmute(v);
if arr[0] > best_val { best_val = arr[0]; best_idx = i; }
if arr[1] > best_val { best_val = arr[1]; best_idx = i + 1; }
i += 2;
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "aarch64")]
pub fn argmax_i64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_i64_neon(_: &[i64]) -> usize { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_i64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_i64_sse(a: &[i64]) -> usize {
if a.is_empty() { return 0; }
let mut best_idx = 0;
let mut best_val = i64::MIN;
for (i, &v) in a.iter().enumerate() {
if v > best_val {
best_val = v;
best_idx = i;
}
}
best_idx
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_i64_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i64_sse(_: &[i64]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_i64_avx2(a: &[i64]) -> usize {
if a.is_empty() { return 0; }
let mut best_idx = 0;
let mut best_val = i64::MIN;
for (i, &v) in a.iter().enumerate() {
if v > best_val {
best_val = v;
best_idx = i;
}
}
best_idx
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_i64_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i64_avx2(_: &[i64]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn argmax_i16_scalar(a: &[i16]) -> usize {
let mut idx = 0;
let mut max = i16::MIN;
for (i, &v) in a.iter().enumerate() {
if v > max {
max = v;
idx = i;
}
}
idx
}
pub fn argmax_i16_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn argmax_i16_neon(a: &[i16]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::aarch64::*;
let mut i = 0;
let mut vmax = vld1q_s16(a.as_ptr());
let mut vidx: uint16x8_t = std::mem::transmute::<[u16; 8], _>([0, 1, 2, 3, 4, 5, 6, 7]);
i += 8;
while i + 8 <= n {
let va = vld1q_s16(a.as_ptr().add(i));
let vcmp = vcgtq_s16(va, vmax);
vmax = vmaxq_s16(vmax, va);
let local_idx: uint16x8_t = std::mem::transmute::<[u16; 8], _>([i as u16, (i + 1) as u16, (i + 2) as u16, (i + 3) as u16, (i + 4) as u16, (i + 5) as u16, (i + 6) as u16, (i + 7) as u16]);
vidx = vbslq_u16(vcmp, local_idx, vidx);
i += 8;
}
let final_vals: [i16; 8] = std::mem::transmute(vmax);
let final_idxs: [u16; 8] = std::mem::transmute(vidx);
let mut best_idx = final_idxs[0] as usize;
let mut best_val = final_vals[0];
for k in 1..8 {
let idx_k = final_idxs[k] as usize;
if idx_k < n && final_vals[k] > best_val {
best_val = final_vals[k];
best_idx = idx_k;
}
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "aarch64")]
pub fn argmax_i16_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_i16_neon(_: &[i16]) -> usize { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_i16_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_i16_sse(a: &[i16]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
let mut vmax = _mm_loadu_si128(a.as_ptr() as *const __m128i);
let mut vidx = _mm_set_epi16(7, 6, 5, 4, 3, 2, 1, 0);
i += 8;
while i + 8 <= n {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let cmp = _mm_cmpgt_epi16(va, vmax);
let local_idx = _mm_set_epi16((i + 7) as i16, (i + 6) as i16, (i + 5) as i16, (i + 4) as i16, (i + 3) as i16, (i + 2) as i16, (i + 1) as i16, i as i16);
vmax = _mm_max_epi16(vmax, va);
vidx = _mm_or_si128(_mm_and_si128(cmp, local_idx), _mm_andnot_si128(cmp, vidx));
i += 8;
}
let vals: [i16; 8] = std::mem::transmute(vmax);
let idxs: [i16; 8] = std::mem::transmute(vidx);
let mut best_idx = idxs[0] as usize;
let mut best_val = vals[0];
for k in 1..8 {
if vals[k] > best_val { best_val = vals[k]; best_idx = idxs[k] as usize; }
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_i16_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i16_sse(_: &[i16]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i16_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_i16_avx2(a: &[i16]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
let mut vmax = _mm256_loadu_si256(a.as_ptr() as *const __m256i);
let mut vidx = _mm256_set_epi16(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
i += 16;
while i + 16 <= n {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let cmp = _mm256_cmpgt_epi16(va, vmax);
let local_idx = _mm256_set_epi16(
(i + 15) as i16, (i + 14) as i16, (i + 13) as i16, (i + 12) as i16,
(i + 11) as i16, (i + 10) as i16, (i + 9) as i16, (i + 8) as i16,
(i + 7) as i16, (i + 6) as i16, (i + 5) as i16, (i + 4) as i16,
(i + 3) as i16, (i + 2) as i16, (i + 1) as i16, i as i16
);
vmax = _mm256_max_epi16(vmax, va);
vidx = _mm256_blendv_epi8(vidx, local_idx, cmp);
i += 16;
}
let vals: [i16; 16] = std::mem::transmute(vmax);
let idxs: [i16; 16] = std::mem::transmute(vidx);
let mut best_idx = idxs[0] as usize;
let mut best_val = vals[0];
for k in 1..16 {
if vals[k] > best_val { best_val = vals[k]; best_idx = idxs[k] as usize; }
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_i16_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i16_avx2(_: &[i16]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i16_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn argmax_i8_scalar(a: &[i8]) -> usize {
let mut idx = 0;
let mut max = i8::MIN;
for (i, &v) in a.iter().enumerate() {
if v > max {
max = v;
idx = i;
}
}
idx
}
pub fn argmax_i8_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn argmax_i8_neon(a: &[i8]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::aarch64::*;
let mut i = 0;
let mut vmax = vld1q_s8(a.as_ptr());
let mut vidx: uint8x16_t = std::mem::transmute::<[u8; 16], _>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
i += 16;
while i + 16 <= n {
let va = vld1q_s8(a.as_ptr().add(i));
let vcmp = vcgtq_s8(va, vmax);
vmax = vmaxq_s8(vmax, va);
let local_idx: uint8x16_t = std::mem::transmute::<[u8; 16], _>([
i as u8, (i + 1) as u8, (i + 2) as u8, (i + 3) as u8,
(i + 4) as u8, (i + 5) as u8, (i + 6) as u8, (i + 7) as u8,
(i + 8) as u8, (i + 9) as u8, (i + 10) as u8, (i + 11) as u8,
(i + 12) as u8, (i + 13) as u8, (i + 14) as u8, (i + 15) as u8
]);
vidx = vbslq_u8(vcmp, local_idx, vidx);
i += 16;
}
let final_vals: [i8; 16] = std::mem::transmute(vmax);
let final_idxs: [u8; 16] = std::mem::transmute(vidx);
let mut best_idx = final_idxs[0] as usize;
let mut best_val = final_vals[0];
for k in 1..16 {
let idx_k = final_idxs[k] as usize;
if idx_k < n && final_vals[k] > best_val {
best_val = final_vals[k];
best_idx = idx_k;
}
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "aarch64")]
pub fn argmax_i8_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_i8_neon(_: &[i8]) -> usize { 0 }
#[cfg(not(target_arch = "aarch64"))]
pub fn argmax_i8_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_i8_sse(a: &[i8]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
let mut vmax = _mm_loadu_si128(a.as_ptr() as *const __m128i);
let mut vidx = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
i += 16;
while i + 16 <= n {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let cmp = _mm_cmpgt_epi8(va, vmax);
let local_idx = _mm_set_epi8(
(i + 15) as i8, (i + 14) as i8, (i + 13) as i8, (i + 12) as i8,
(i + 11) as i8, (i + 10) as i8, (i + 9) as i8, (i + 8) as i8,
(i + 7) as i8, (i + 6) as i8, (i + 5) as i8, (i + 4) as i8,
(i + 3) as i8, (i + 2) as i8, (i + 1) as i8, i as i8
);
vmax = _mm_max_epi8(vmax, va);
vidx = _mm_or_si128(_mm_and_si128(cmp, local_idx), _mm_andnot_si128(cmp, vidx));
i += 16;
}
let vals: [i8; 16] = std::mem::transmute(vmax);
let idxs: [i8; 16] = std::mem::transmute(vidx);
let mut best_idx = idxs[0] as usize;
let mut best_val = vals[0];
for k in 1..16 {
if vals[k] > best_val { best_val = vals[k]; best_idx = idxs[k] as usize; }
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_i8_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i8_sse(_: &[i8]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i8_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn argmax_i8_avx2(a: &[i8]) -> usize {
let n = a.len();
if n == 0 { return 0; }
unsafe {
use std::arch::x86_64::*;
let mut i = 0;
let mut vmax = _mm256_loadu_si256(a.as_ptr() as *const __m256i);
let mut vidx = _mm256_set_epi8(
31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
);
i += 32;
while i + 32 <= n {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let cmp = _mm256_cmpgt_epi8(va, vmax);
let local_idx = _mm256_set_epi8(
(i + 31) as i8, (i + 30) as i8, (i + 29) as i8, (i + 28) as i8,
(i + 27) as i8, (i + 26) as i8, (i + 25) as i8, (i + 24) as i8,
(i + 23) as i8, (i + 22) as i8, (i + 21) as i8, (i + 20) as i8,
(i + 19) as i8, (i + 18) as i8, (i + 17) as i8, (i + 16) as i8,
(i + 15) as i8, (i + 14) as i8, (i + 13) as i8, (i + 12) as i8,
(i + 11) as i8, (i + 10) as i8, (i + 9) as i8, (i + 8) as i8,
(i + 7) as i8, (i + 6) as i8, (i + 5) as i8, (i + 4) as i8,
(i + 3) as i8, (i + 2) as i8, (i + 1) as i8, i as i8
);
vmax = _mm256_max_epi8(vmax, va);
vidx = _mm256_blendv_epi8(vidx, local_idx, cmp);
i += 32;
}
let vals: [i8; 32] = std::mem::transmute(vmax);
let idxs: [i8; 32] = std::mem::transmute(vidx);
let mut best_idx = idxs[0] as usize;
let mut best_val = vals[0];
for k in 1..32 {
if vals[k] > best_val { best_val = vals[k]; best_idx = idxs[k] as usize; }
}
for (j, &val) in a.iter().enumerate().skip(i) {
if val > best_val { best_val = val; best_idx = j; }
}
best_idx
}
}
#[cfg(target_arch = "x86_64")]
pub fn argmax_i8_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i8_avx2(_: &[i8]) -> usize { 0 }
#[cfg(not(target_arch = "x86_64"))]
pub fn argmax_i8_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn hadamard_product_i32_scalar(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_mul(b[i]);
}
}
pub fn hadamard_product_i32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_i32_neon(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 4 <= len {
let va = vld1q_s32(a.as_ptr().add(i));
let vb = vld1q_s32(b.as_ptr().add(i));
vst1q_s32(c.as_mut_ptr().add(i), vmulq_s32(va, vb));
i += 4;
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_i32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_i32_neon(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_i32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i32_sse(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse4.1") {
while i + 4 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_mullo_epi32(va, vb));
i += 4;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i32_sse(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i32_avx2(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 8 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_mullo_epi32(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i32_avx2(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn hadamard_product_i64_scalar(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_mul(b[i]);
}
}
pub fn hadamard_product_i64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_i64_neon(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 2 <= len {
let va = vld1q_s64(a.as_ptr().add(i));
let vb = vld1q_s64(b.as_ptr().add(i));
let arr_a: [i64; 2] = std::mem::transmute(va);
let arr_b: [i64; 2] = std::mem::transmute(vb);
let p0 = arr_a[0].wrapping_mul(arr_b[0]);
let p1 = arr_a[1].wrapping_mul(arr_b[1]);
let vp: int64x2_t = std::mem::transmute([p0, p1]);
vst1q_s64(c.as_mut_ptr().add(i), vp);
i += 2;
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_i64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_i64_neon(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_i64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i64_sse(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 2 <= len {
let p0 = a[i].wrapping_mul(b[i]);
let p1 = a[i + 1].wrapping_mul(b[i + 1]);
let vp = _mm_set_epi64x(p1, p0);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, vp);
i += 2;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i64_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i64_sse(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i64_avx2(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 4 <= len {
let p0 = a[i].wrapping_mul(b[i]);
let p1 = a[i + 1].wrapping_mul(b[i + 1]);
let p2 = a[i + 2].wrapping_mul(b[i + 2]);
let p3 = a[i + 3].wrapping_mul(b[i + 3]);
let vp = _mm256_set_epi64x(p3, p2, p1, p0);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, vp);
i += 4;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i64_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i64_avx2(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn hadamard_product_i16_scalar(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_mul(b[i]);
}
}
pub fn hadamard_product_i16_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_i16_neon(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 8 <= len {
let va = vld1q_s16(a.as_ptr().add(i));
let vb = vld1q_s16(b.as_ptr().add(i));
vst1q_s16(c.as_mut_ptr().add(i), vmulq_s16(va, vb));
i += 8;
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_i16_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_i16_neon(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_i16_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i16_sse(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 8 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_mullo_epi16(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i16_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i16_sse(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i16_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i16_avx2(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 16 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_mullo_epi16(va, vb));
i += 16;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i16_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i16_avx2(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i16_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn hadamard_product_i8_scalar(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_mul(b[i]);
}
}
pub fn hadamard_product_i8_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_i8_neon(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 16 <= len {
let va = vld1q_s8(a.as_ptr().add(i));
let vb = vld1q_s8(b.as_ptr().add(i));
vst1q_s8(c.as_mut_ptr().add(i), vmulq_s8(va, vb));
i += 16;
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn hadamard_product_i8_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_i8_neon(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn hadamard_product_i8_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i8_sse(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse4.1") {
while i + 16 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
let va_lo = _mm_cvtepi8_epi16(va);
let va_hi = _mm_cvtepi8_epi16(_mm_srli_si128(va, 8));
let vb_lo = _mm_cvtepi8_epi16(vb);
let vb_hi = _mm_cvtepi8_epi16(_mm_srli_si128(vb, 8));
let prod_lo = _mm_mullo_epi16(va_lo, vb_lo);
let prod_hi = _mm_mullo_epi16(va_hi, vb_hi);
let mask_lo = _mm_setr_epi8(0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1);
let mask_hi = _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 4, 6, 8, 10, 12, 14);
let lo = _mm_shuffle_epi8(prod_lo, mask_lo);
let hi = _mm_shuffle_epi8(prod_hi, mask_hi);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_or_si128(lo, hi));
i += 16;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i8_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i8_sse(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i8_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i8_avx2(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 32 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
let va_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(va));
let va_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(va, 1));
let vb_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(vb));
let vb_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(vb, 1));
let prod_lo = _mm256_mullo_epi16(va_lo, vb_lo);
let prod_hi = _mm256_mullo_epi16(va_hi, vb_hi);
let mask_lo = _mm256_setr_epi8(0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1);
let mask_hi = _mm256_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 4, 6, 8, 10, 12, 14);
let lo = _mm256_shuffle_epi8(prod_lo, mask_lo);
let hi = _mm256_shuffle_epi8(prod_hi, mask_hi);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_or_si256(lo, hi));
i += 32;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn hadamard_product_i8_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i8_avx2(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn hadamard_product_i8_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn add_i32_scalar(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_add(b[i]);
}
}
pub fn add_i32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn add_i32_neon(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 4 <= len {
let va = vld1q_s32(a.as_ptr().add(i));
let vb = vld1q_s32(b.as_ptr().add(i));
vst1q_s32(c.as_mut_ptr().add(i), vaddq_s32(va, vb));
i += 4;
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn add_i32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn add_i32_neon(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn add_i32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn add_i32_sse(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 4 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_add_epi32(va, vb));
i += 4;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn add_i32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i32_sse(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn add_i32_avx2(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 8 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_add_epi32(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn add_i32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i32_avx2(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn add_i64_scalar(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_add(b[i]);
}
}
pub fn add_i64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn add_i64_neon(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 2 <= len {
let va = vld1q_s64(a.as_ptr().add(i));
let vb = vld1q_s64(b.as_ptr().add(i));
vst1q_s64(c.as_mut_ptr().add(i), vaddq_s64(va, vb));
i += 2;
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn add_i64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn add_i64_neon(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn add_i64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn add_i64_sse(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 2 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_add_epi64(va, vb));
i += 2;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn add_i64_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i64_sse(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn add_i64_avx2(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 4 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_add_epi64(va, vb));
i += 4;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn add_i64_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i64_avx2(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn add_i16_scalar(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_add(b[i]);
}
}
pub fn add_i16_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn add_i16_neon(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 8 <= len {
let va = vld1q_s16(a.as_ptr().add(i));
let vb = vld1q_s16(b.as_ptr().add(i));
vst1q_s16(c.as_mut_ptr().add(i), vaddq_s16(va, vb));
i += 8;
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn add_i16_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn add_i16_neon(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn add_i16_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn add_i16_sse(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 8 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_add_epi16(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn add_i16_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i16_sse(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i16_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn add_i16_avx2(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 16 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_add_epi16(va, vb));
i += 16;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn add_i16_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i16_avx2(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i16_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn add_i8_scalar(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_add(b[i]);
}
}
pub fn add_i8_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn add_i8_neon(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 16 <= len {
let va = vld1q_s8(a.as_ptr().add(i));
let vb = vld1q_s8(b.as_ptr().add(i));
vst1q_s8(c.as_mut_ptr().add(i), vaddq_s8(va, vb));
i += 16;
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn add_i8_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn add_i8_neon(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn add_i8_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn add_i8_sse(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 16 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_add_epi8(va, vb));
i += 16;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn add_i8_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i8_sse(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i8_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn add_i8_avx2(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 32 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_add_epi8(va, vb));
i += 32;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_add(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn add_i8_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i8_avx2(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn add_i8_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn sub_i32_scalar(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_sub(b[i]);
}
}
pub fn sub_i32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn sub_i32_neon(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 4 <= len {
let va = vld1q_s32(a.as_ptr().add(i));
let vb = vld1q_s32(b.as_ptr().add(i));
vst1q_s32(c.as_mut_ptr().add(i), vsubq_s32(va, vb));
i += 4;
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn sub_i32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn sub_i32_neon(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn sub_i32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn sub_i32_sse(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 4 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_sub_epi32(va, vb));
i += 4;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn sub_i32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i32_sse(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn sub_i32_avx2(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 8 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_sub_epi32(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn sub_i32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i32_avx2(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn sub_i64_scalar(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_sub(b[i]);
}
}
pub fn sub_i64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn sub_i64_neon(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 2 <= len {
let va = vld1q_s64(a.as_ptr().add(i));
let vb = vld1q_s64(b.as_ptr().add(i));
vst1q_s64(c.as_mut_ptr().add(i), vsubq_s64(va, vb));
i += 2;
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn sub_i64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn sub_i64_neon(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn sub_i64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn sub_i64_sse(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 2 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_sub_epi64(va, vb));
i += 2;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn sub_i64_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i64_sse(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn sub_i64_avx2(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 4 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_sub_epi64(va, vb));
i += 4;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn sub_i64_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i64_avx2(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn sub_i16_scalar(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_sub(b[i]);
}
}
pub fn sub_i16_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn sub_i16_neon(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 8 <= len {
let va = vld1q_s16(a.as_ptr().add(i));
let vb = vld1q_s16(b.as_ptr().add(i));
vst1q_s16(c.as_mut_ptr().add(i), vsubq_s16(va, vb));
i += 8;
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn sub_i16_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn sub_i16_neon(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn sub_i16_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn sub_i16_sse(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 8 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_sub_epi16(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn sub_i16_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i16_sse(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i16_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn sub_i16_avx2(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 16 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_sub_epi16(va, vb));
i += 16;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn sub_i16_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i16_avx2(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i16_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn sub_i8_scalar(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_sub(b[i]);
}
}
pub fn sub_i8_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn sub_i8_neon(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 16 <= len {
let va = vld1q_s8(a.as_ptr().add(i));
let vb = vld1q_s8(b.as_ptr().add(i));
vst1q_s8(c.as_mut_ptr().add(i), vsubq_s8(va, vb));
i += 16;
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn sub_i8_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn sub_i8_neon(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn sub_i8_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn sub_i8_sse(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 16 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_sub_epi8(va, vb));
i += 16;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn sub_i8_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i8_sse(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i8_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn sub_i8_avx2(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 32 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_sub_epi8(va, vb));
i += 32;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_sub(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn sub_i8_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i8_avx2(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn sub_i8_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn mul_i32_scalar(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_mul(b[i]);
}
}
pub fn mul_i32_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn mul_i32_neon(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 4 <= len {
let va = vld1q_s32(a.as_ptr().add(i));
let vb = vld1q_s32(b.as_ptr().add(i));
vst1q_s32(c.as_mut_ptr().add(i), vmulq_s32(va, vb));
i += 4;
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn mul_i32_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn mul_i32_neon(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn mul_i32_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn mul_i32_sse(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse4.1") {
while i + 4 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_mullo_epi32(va, vb));
i += 4;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn mul_i32_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i32_sse(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i32_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn mul_i32_avx2(a: &[i32], b: &[i32], c: &mut [i32]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 8 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_mullo_epi32(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn mul_i32_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i32_avx2(_: &[i32], _: &[i32], _: &mut [i32]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i32_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn mul_i64_scalar(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_mul(b[i]);
}
}
pub fn mul_i64_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn mul_i64_neon(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 2 <= len {
let va = vld1q_s64(a.as_ptr().add(i));
let vb = vld1q_s64(b.as_ptr().add(i));
let arr_a: [i64; 2] = std::mem::transmute(va);
let arr_b: [i64; 2] = std::mem::transmute(vb);
let p0 = arr_a[0].wrapping_mul(arr_b[0]);
let p1 = arr_a[1].wrapping_mul(arr_b[1]);
let vp: int64x2_t = std::mem::transmute([p0, p1]);
vst1q_s64(c.as_mut_ptr().add(i), vp);
i += 2;
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn mul_i64_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn mul_i64_neon(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn mul_i64_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn mul_i64_sse(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 2 <= len {
let p0 = a[i].wrapping_mul(b[i]);
let p1 = a[i + 1].wrapping_mul(b[i + 1]);
let vp = _mm_set_epi64x(p1, p0);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, vp);
i += 2;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn mul_i64_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i64_sse(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i64_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn mul_i64_avx2(a: &[i64], b: &[i64], c: &mut [i64]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 4 <= len {
let p0 = a[i].wrapping_mul(b[i]);
let p1 = a[i + 1].wrapping_mul(b[i + 1]);
let p2 = a[i + 2].wrapping_mul(b[i + 2]);
let p3 = a[i + 3].wrapping_mul(b[i + 3]);
let vp = _mm256_set_epi64x(p3, p2, p1, p0);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, vp);
i += 4;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn mul_i64_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i64_avx2(_: &[i64], _: &[i64], _: &mut [i64]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i64_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn mul_i16_scalar(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_mul(b[i]);
}
}
pub fn mul_i16_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn mul_i16_neon(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 8 <= len {
let va = vld1q_s16(a.as_ptr().add(i));
let vb = vld1q_s16(b.as_ptr().add(i));
vst1q_s16(c.as_mut_ptr().add(i), vmulq_s16(va, vb));
i += 8;
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn mul_i16_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn mul_i16_neon(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn mul_i16_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn mul_i16_sse(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse2") {
while i + 8 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_mullo_epi16(va, vb));
i += 8;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn mul_i16_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE2" || f == "sse2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i16_sse(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i16_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn mul_i16_avx2(a: &[i16], b: &[i16], c: &mut [i16]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 16 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_mullo_epi16(va, vb));
i += 16;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn mul_i16_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i16_avx2(_: &[i16], _: &[i16], _: &mut [i16]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i16_avx2_supported(_: &HardwareDNA) -> bool { false }
pub fn mul_i8_scalar(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
for i in 0..len {
c[i] = a[i].wrapping_mul(b[i]);
}
}
pub fn mul_i8_supported(_: &HardwareDNA) -> bool { true }
#[cfg(target_arch = "aarch64")]
pub fn mul_i8_neon(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::aarch64::*;
while i + 16 <= len {
let va = vld1q_s8(a.as_ptr().add(i));
let vb = vld1q_s8(b.as_ptr().add(i));
vst1q_s8(c.as_mut_ptr().add(i), vmulq_s8(va, vb));
i += 16;
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "aarch64")]
pub fn mul_i8_neon_supported(_: &HardwareDNA) -> bool { true }
#[cfg(not(target_arch = "aarch64"))]
pub fn mul_i8_neon(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "aarch64"))]
pub fn mul_i8_neon_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn mul_i8_sse(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("sse4.1") {
while i + 16 <= len {
let va = _mm_loadu_si128(a.as_ptr().add(i) as *const __m128i);
let vb = _mm_loadu_si128(b.as_ptr().add(i) as *const __m128i);
let va_lo = _mm_cvtepi8_epi16(va);
let va_hi = _mm_cvtepi8_epi16(_mm_srli_si128(va, 8));
let vb_lo = _mm_cvtepi8_epi16(vb);
let vb_hi = _mm_cvtepi8_epi16(_mm_srli_si128(vb, 8));
let prod_lo = _mm_mullo_epi16(va_lo, vb_lo);
let prod_hi = _mm_mullo_epi16(va_hi, vb_hi);
let mask_lo = _mm_setr_epi8(0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1);
let mask_hi = _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 4, 6, 8, 10, 12, 14);
let lo = _mm_shuffle_epi8(prod_lo, mask_lo);
let hi = _mm_shuffle_epi8(prod_hi, mask_hi);
_mm_storeu_si128(c.as_mut_ptr().add(i) as *mut __m128i, _mm_or_si128(lo, hi));
i += 16;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn mul_i8_sse_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "SSE4.1" || f == "sse4.1")
}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i8_sse(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i8_sse_supported(_: &HardwareDNA) -> bool { false }
#[cfg(target_arch = "x86_64")]
pub fn mul_i8_avx2(a: &[i8], b: &[i8], c: &mut [i8]) {
let len = a.len().min(b.len()).min(c.len());
let mut i = 0;
unsafe {
use std::arch::x86_64::*;
if is_x86_feature_detected!("avx2") {
while i + 32 <= len {
let va = _mm256_loadu_si256(a.as_ptr().add(i) as *const __m256i);
let vb = _mm256_loadu_si256(b.as_ptr().add(i) as *const __m256i);
let va_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(va));
let va_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(va, 1));
let vb_lo = _mm256_cvtepi8_epi16(_mm256_castsi256_si128(vb));
let vb_hi = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(vb, 1));
let prod_lo = _mm256_mullo_epi16(va_lo, vb_lo);
let prod_hi = _mm256_mullo_epi16(va_hi, vb_hi);
let mask_lo = _mm256_setr_epi8(0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1);
let mask_hi = _mm256_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 4, 6, 8, 10, 12, 14, -1, -1, -1, -1, -1, -1, -1, -1, 0, 2, 4, 6, 8, 10, 12, 14);
let lo = _mm256_shuffle_epi8(prod_lo, mask_lo);
let hi = _mm256_shuffle_epi8(prod_hi, mask_hi);
_mm256_storeu_si256(c.as_mut_ptr().add(i) as *mut __m256i, _mm256_or_si256(lo, hi));
i += 32;
}
}
}
for j in i..len {
c[j] = a[j].wrapping_mul(b[j]);
}
}
#[cfg(target_arch = "x86_64")]
pub fn mul_i8_avx2_supported(dna: &HardwareDNA) -> bool {
dna.cpu.features.iter().any(|f| f == "AVX2" || f == "avx2")
}
pub fn negate_i32_scalar(a: &[i32], out: &mut [i32]) {
let len = a.len().min(out.len());
for i in 0..len {
out[i] = -a[i];
}
}
pub fn clamp_i32_scalar(a: &[i32], lo: i32, hi: i32, out: &mut [i32]) {
let len = a.len().min(out.len());
for i in 0..len {
out[i] = a[i].clamp(lo, hi);
}
}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i8_avx2(_: &[i8], _: &[i8], _: &mut [i8]) {}
#[cfg(not(target_arch = "x86_64"))]
pub fn mul_i8_avx2_supported(_: &HardwareDNA) -> bool { false }