use cfg_if::cfg_if;
pub const HAS_XMUL: bool = {
cfg_if! {
if #[cfg(any(
all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
),
all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
)
))] {
true
} else {
false
}
}
};
#[cfg(any(
all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
),
all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
)
))]
#[inline]
pub fn xmul8(a: u8, b: u8) -> (u8, u8) {
cfg_if! {
if #[cfg(all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
))] {
use core::arch::x86_64::*;
unsafe {
let a = _mm_set_epi64x(0, a as i64);
let b = _mm_set_epi64x(0, b as i64);
let x = _mm_clmulepi64_si128::<0>(a, b);
let lo = _mm_extract_epi64::<0>(x) as u64;
(lo as u8, (lo >> 8) as u8)
}
} else if #[cfg(all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
))] {
use core::arch::aarch64::*;
unsafe {
let x = vmull_p64(a as u64, b as u64);
(x as u8, (x >> 8) as u8)
}
}
}
}
#[cfg(any(
all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
),
all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
)
))]
#[inline]
pub fn xmul16(a: u16, b: u16) -> (u16, u16) {
cfg_if! {
if #[cfg(all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
))] {
use core::arch::x86_64::*;
unsafe {
let a = _mm_set_epi64x(0, a as i64);
let b = _mm_set_epi64x(0, b as i64);
let x = _mm_clmulepi64_si128::<0>(a, b);
let lo = _mm_extract_epi64::<0>(x) as u64;
(lo as u16, (lo >> 16) as u16)
}
} else if #[cfg(all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
))] {
use core::arch::aarch64::*;
unsafe {
let x = vmull_p64(a as u64, b as u64);
(x as u16, (x >> 16) as u16)
}
}
}
}
#[cfg(any(
all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
),
all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
)
))]
#[inline]
pub fn xmul32(a: u32, b: u32) -> (u32, u32) {
cfg_if! {
if #[cfg(all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
))] {
use core::arch::x86_64::*;
unsafe {
let a = _mm_set_epi64x(0, a as i64);
let b = _mm_set_epi64x(0, b as i64);
let x = _mm_clmulepi64_si128::<0>(a, b);
let lo = _mm_extract_epi64::<0>(x) as u64;
(lo as u32, (lo >> 32) as u32)
}
} else if #[cfg(all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
))] {
use core::arch::aarch64::*;
unsafe {
let x = vmull_p64(a as u64, b as u64);
(x as u32, (x >> 32) as u32)
}
}
}
}
#[cfg(any(
all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
),
all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
)
))]
#[inline]
pub fn xmul64(a: u64, b: u64) -> (u64, u64) {
cfg_if! {
if #[cfg(all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
))] {
use core::arch::x86_64::*;
unsafe {
let a = _mm_set_epi64x(0, a as i64);
let b = _mm_set_epi64x(0, b as i64);
let x = _mm_clmulepi64_si128::<0>(a, b);
let lo = _mm_extract_epi64::<0>(x) as u64;
let hi = _mm_extract_epi64::<1>(x) as u64;
(lo, hi)
}
} else if #[cfg(all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
))] {
use core::arch::aarch64::*;
unsafe {
let x = vmull_p64(a as u64, b as u64);
(x as u64, (x >> 64) as u64)
}
}
}
}
#[cfg(any(
all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
),
all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
)
))]
#[inline]
pub fn xmul128(a: u128, b: u128) -> (u128, u128) {
cfg_if! {
if #[cfg(all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
))] {
use core::arch::x86_64::*;
unsafe {
let a = _mm_set_epi64x((a >> 64) as i64, a as i64);
let b = _mm_set_epi64x((b >> 64) as i64, b as i64);
let x = _mm_clmulepi64_si128::<0x00>(a, b);
let y = _mm_clmulepi64_si128::<0x01>(a, b);
let z = _mm_clmulepi64_si128::<0x10>(a, b);
let w = _mm_clmulepi64_si128::<0x11>(a, b);
let lolo = _mm_extract_epi64::<0>(x) as u64;
let lohi = (_mm_extract_epi64::<1>(x) as u64)
^ (_mm_extract_epi64::<0>(y) as u64)
^ (_mm_extract_epi64::<0>(z) as u64);
let hilo = (_mm_extract_epi64::<0>(w) as u64)
^ (_mm_extract_epi64::<1>(y) as u64)
^ (_mm_extract_epi64::<1>(z) as u64);
let hihi = _mm_extract_epi64::<1>(w) as u64;
let lo = ((lohi as u128) << 64) | (lolo as u128);
let hi = ((hihi as u128) << 64) | (hilo as u128);
(lo, hi)
}
} else if #[cfg(all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
))] {
use core::arch::aarch64::*;
unsafe {
let x = vmull_p64(a as u64, b as u64);
let y = vmull_p64((a >> 64) as u64, (b >> 0) as u64);
let z = vmull_p64((a >> 0) as u64, (b >> 64) as u64);
let w = vmull_p64((a >> 64) as u64, (b >> 64) as u64);
(x ^ (y << 64) ^ (z << 64), w ^ (y >> 64) ^ (z >> 64))
}
}
}
}
#[cfg(test)]
mod test {
#[allow(unused)]
use super::*;
#[cfg(any(
all(
not(feature="no-xmul"),
target_arch="x86_64",
target_feature="pclmulqdq"
),
all(
not(feature="no-xmul"),
feature="nightly",
target_arch="aarch64",
target_feature="neon"
)
))]
#[test]
fn xmul() {
assert_eq!(xmul8(0x12, 0x12), (0x04, 0x01));
assert_eq!(xmul16(0x1234, 0x1234), (0x0510, 0x0104));
assert_eq!(xmul32(0x12345678, 0x12345678), (0x11141540, 0x01040510));
assert_eq!(xmul64(0x123456789abcdef1, 0x123456789abcdef1), (0x4144455051545501, 0x0104051011141540));
assert_eq!(xmul128(0x123456789abcdef123456789abcdef12, 0x123456789abcdef123456789abcdef12), (0x04051011141540414445505154550104, 0x01040510111415404144455051545501));
}
}