diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /library/stdarch/crates/core_arch/src/arm/dsp.rs | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | library/stdarch/crates/core_arch/src/arm/dsp.rs | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/library/stdarch/crates/core_arch/src/arm/dsp.rs b/library/stdarch/crates/core_arch/src/arm/dsp.rs new file mode 100644 index 000000000..6720f97a5 --- /dev/null +++ b/library/stdarch/crates/core_arch/src/arm/dsp.rs @@ -0,0 +1,384 @@ +//! # References: +//! +//! - Section 8.3 "16-bit multiplications" +//! +//! Intrinsics that could live here: +//! +//! - \[x\] __smulbb +//! - \[x\] __smulbt +//! - \[x\] __smultb +//! - \[x\] __smultt +//! - \[x\] __smulwb +//! - \[x\] __smulwt +//! - \[x\] __qadd +//! - \[x\] __qsub +//! - \[x\] __qdbl +//! - \[x\] __smlabb +//! - \[x\] __smlabt +//! - \[x\] __smlatb +//! - \[x\] __smlatt +//! - \[x\] __smlawb +//! - \[x\] __smlawt + +#[cfg(test)] +use stdarch_test::assert_instr; + +use crate::mem::transmute; + +types! { + /// ARM-specific 32-bit wide vector of two packed `i16`. + pub struct int16x2_t(i16, i16); + /// ARM-specific 32-bit wide vector of two packed `u16`. + pub struct uint16x2_t(u16, u16); +} + +extern "unadjusted" { + #[link_name = "llvm.arm.smulbb"] + fn arm_smulbb(a: i32, b: i32) -> i32; + + #[link_name = "llvm.arm.smulbt"] + fn arm_smulbt(a: i32, b: i32) -> i32; + + #[link_name = "llvm.arm.smultb"] + fn arm_smultb(a: i32, b: i32) -> i32; + + #[link_name = "llvm.arm.smultt"] + fn arm_smultt(a: i32, b: i32) -> i32; + + #[link_name = "llvm.arm.smulwb"] + fn arm_smulwb(a: i32, b: i32) -> i32; + + #[link_name = "llvm.arm.smulwt"] + fn arm_smulwt(a: i32, b: i32) -> i32; + + #[link_name = "llvm.arm.qadd"] + fn arm_qadd(a: i32, b: i32) -> i32; + + #[link_name = "llvm.arm.qsub"] + fn arm_qsub(a: i32, b: i32) -> i32; + + #[link_name = "llvm.arm.smlabb"] + fn arm_smlabb(a: i32, b: i32, c: i32) -> i32; + + #[link_name = "llvm.arm.smlabt"] + fn arm_smlabt(a: i32, b: i32, c: i32) -> i32; + + #[link_name = "llvm.arm.smlatb"] + fn arm_smlatb(a: i32, b: i32, c: i32) -> i32; + + #[link_name = "llvm.arm.smlatt"] + fn arm_smlatt(a: i32, b: i32, c: i32) -> i32; + + #[link_name = "llvm.arm.smlawb"] + fn arm_smlawb(a: i32, b: i32, c: i32) -> i32; + + #[link_name = "llvm.arm.smlawt"] + fn arm_smlawt(a: i32, b: i32, c: i32) -> i32; +} + +/// Insert a SMULBB instruction +/// +/// Returns the equivalent of a\[0\] * b\[0\] +/// where \[0\] is the lower 16 bits and \[1\] is the upper 16 bits. +#[inline] +#[cfg_attr(test, assert_instr(smulbb))] +pub unsafe fn __smulbb(a: int16x2_t, b: int16x2_t) -> i32 { + arm_smulbb(transmute(a), transmute(b)) +} + +/// Insert a SMULTB instruction +/// +/// Returns the equivalent of a\[0\] * b\[1\] +/// where \[0\] is the lower 16 bits and \[1\] is the upper 16 bits. +#[inline] +#[cfg_attr(test, assert_instr(smultb))] +pub unsafe fn __smultb(a: int16x2_t, b: int16x2_t) -> i32 { + arm_smultb(transmute(a), transmute(b)) +} + +/// Insert a SMULTB instruction +/// +/// Returns the equivalent of a\[1\] * b\[0\] +/// where \[0\] is the lower 16 bits and \[1\] is the upper 16 bits. +#[inline] +#[cfg_attr(test, assert_instr(smulbt))] +pub unsafe fn __smulbt(a: int16x2_t, b: int16x2_t) -> i32 { + arm_smulbt(transmute(a), transmute(b)) +} + +/// Insert a SMULTT instruction +/// +/// Returns the equivalent of a\[1\] * b\[1\] +/// where \[0\] is the lower 16 bits and \[1\] is the upper 16 bits. +#[inline] +#[cfg_attr(test, assert_instr(smultt))] +pub unsafe fn __smultt(a: int16x2_t, b: int16x2_t) -> i32 { + arm_smultt(transmute(a), transmute(b)) +} + +/// Insert a SMULWB instruction +/// +/// Multiplies the 32-bit signed first operand with the low halfword +/// (as a 16-bit signed integer) of the second operand. +/// Return the top 32 bits of the 48-bit product +#[inline] +#[cfg_attr(test, assert_instr(smulwb))] +pub unsafe fn __smulwb(a: int16x2_t, b: i32) -> i32 { + arm_smulwb(transmute(a), b) +} + +/// Insert a SMULWT instruction +/// +/// Multiplies the 32-bit signed first operand with the high halfword +/// (as a 16-bit signed integer) of the second operand. +/// Return the top 32 bits of the 48-bit product +#[inline] +#[cfg_attr(test, assert_instr(smulwt))] +pub unsafe fn __smulwt(a: int16x2_t, b: i32) -> i32 { + arm_smulwt(transmute(a), b) +} + +/// Signed saturating addition +/// +/// Returns the 32-bit saturating signed equivalent of a + b. +/// Sets the Q flag if saturation occurs. +#[inline] +#[cfg_attr(test, assert_instr(qadd))] +pub unsafe fn __qadd(a: i32, b: i32) -> i32 { + arm_qadd(a, b) +} + +/// Signed saturating subtraction +/// +/// Returns the 32-bit saturating signed equivalent of a - b. +/// Sets the Q flag if saturation occurs. +#[inline] +#[cfg_attr(test, assert_instr(qsub))] +pub unsafe fn __qsub(a: i32, b: i32) -> i32 { + arm_qsub(a, b) +} + +/// Insert a QADD instruction +/// +/// Returns the 32-bit saturating signed equivalent of a + a +/// Sets the Q flag if saturation occurs. +#[inline] +#[cfg_attr(test, assert_instr(qadd))] +pub unsafe fn __qdbl(a: i32) -> i32 { + arm_qadd(a, a) +} + +/// Insert a SMLABB instruction +/// +/// Returns the equivalent of a\[0\] * b\[0\] + c +/// where \[0\] is the lower 16 bits and \[1\] is the upper 16 bits. +/// Sets the Q flag if overflow occurs on the addition. +#[inline] +#[cfg_attr(test, assert_instr(smlabb))] +pub unsafe fn __smlabb(a: int16x2_t, b: int16x2_t, c: i32) -> i32 { + arm_smlabb(transmute(a), transmute(b), c) +} + +/// Insert a SMLABT instruction +/// +/// Returns the equivalent of a\[0\] * b\[1\] + c +/// where \[0\] is the lower 16 bits and \[1\] is the upper 16 bits. +/// Sets the Q flag if overflow occurs on the addition. +#[inline] +#[cfg_attr(test, assert_instr(smlabt))] +pub unsafe fn __smlabt(a: int16x2_t, b: int16x2_t, c: i32) -> i32 { + arm_smlabt(transmute(a), transmute(b), c) +} + +/// Insert a SMLATB instruction +/// +/// Returns the equivalent of a\[1\] * b\[0\] + c +/// where \[0\] is the lower 16 bits and \[1\] is the upper 16 bits. +/// Sets the Q flag if overflow occurs on the addition. +#[inline] +#[cfg_attr(test, assert_instr(smlatb))] +pub unsafe fn __smlatb(a: int16x2_t, b: int16x2_t, c: i32) -> i32 { + arm_smlatb(transmute(a), transmute(b), c) +} + +/// Insert a SMLATT instruction +/// +/// Returns the equivalent of a\[1\] * b\[1\] + c +/// where \[0\] is the lower 16 bits and \[1\] is the upper 16 bits. +/// Sets the Q flag if overflow occurs on the addition. +#[inline] +#[cfg_attr(test, assert_instr(smlatt))] +pub unsafe fn __smlatt(a: int16x2_t, b: int16x2_t, c: i32) -> i32 { + arm_smlatt(transmute(a), transmute(b), c) +} + +/// Insert a SMLAWB instruction +/// +/// Returns the equivalent of (a * b\[0\] + (c << 16)) >> 16 +/// where \[0\] is the lower 16 bits and \[1\] is the upper 16 bits. +/// Sets the Q flag if overflow occurs on the addition. +#[inline] +#[cfg_attr(test, assert_instr(smlawb))] +pub unsafe fn __smlawb(a: i32, b: int16x2_t, c: i32) -> i32 { + arm_smlawb(a, transmute(b), c) +} + +/// Insert a SMLAWT instruction +/// +/// Returns the equivalent of (a * b\[1\] + (c << 16)) >> 16 +/// where \[0\] is the lower 16 bits and \[1\] is the upper 16 bits. +/// Sets the Q flag if overflow occurs on the addition. +#[inline] +#[cfg_attr(test, assert_instr(smlawt))] +pub unsafe fn __smlawt(a: i32, b: int16x2_t, c: i32) -> i32 { + arm_smlawt(a, transmute(b), c) +} + +#[cfg(test)] +mod tests { + use crate::core_arch::{ + arm::*, + simd::{i16x2, i8x4, u8x4}, + }; + use std::mem::transmute; + use stdarch_test::simd_test; + + #[test] + fn smulbb() { + unsafe { + let a = i16x2::new(10, 20); + let b = i16x2::new(30, 40); + assert_eq!(super::__smulbb(transmute(a), transmute(b)), 10 * 30); + } + } + + #[test] + fn smulbt() { + unsafe { + let a = i16x2::new(10, 20); + let b = i16x2::new(30, 40); + assert_eq!(super::__smulbt(transmute(a), transmute(b)), 10 * 40); + } + } + + #[test] + fn smultb() { + unsafe { + let a = i16x2::new(10, 20); + let b = i16x2::new(30, 40); + assert_eq!(super::__smultb(transmute(a), transmute(b)), 20 * 30); + } + } + + #[test] + fn smultt() { + unsafe { + let a = i16x2::new(10, 20); + let b = i16x2::new(30, 40); + assert_eq!(super::__smultt(transmute(a), transmute(b)), 20 * 40); + } + } + + #[test] + fn smulwb() { + unsafe { + let a = i16x2::new(10, 20); + let b = 30; + assert_eq!(super::__smulwb(transmute(a), b), 20 * b); + } + } + + #[test] + fn smulwt() { + unsafe { + let a = i16x2::new(10, 20); + let b = 30; + assert_eq!(super::__smulwt(transmute(a), b), (10 * b) >> 16); + } + } + + #[test] + fn qadd() { + unsafe { + assert_eq!(super::__qadd(-10, 60), 50); + assert_eq!(super::__qadd(i32::MAX, 10), i32::MAX); + assert_eq!(super::__qadd(i32::MIN, -10), i32::MIN); + } + } + + #[test] + fn qsub() { + unsafe { + assert_eq!(super::__qsub(10, 60), -50); + assert_eq!(super::__qsub(i32::MAX, -10), i32::MAX); + assert_eq!(super::__qsub(i32::MIN, 10), i32::MIN); + } + } + + fn qdbl() { + unsafe { + assert_eq!(super::__qdbl(10), 20); + assert_eq!(super::__qdbl(i32::MAX), i32::MAX); + } + } + + fn smlabb() { + unsafe { + let a = i16x2::new(10, 20); + let b = i16x2::new(30, 40); + let c = 50; + let r = (10 * 30) + c; + assert_eq!(super::__smlabb(transmute(a), transmute(b), c), r); + } + } + + fn smlabt() { + unsafe { + let a = i16x2::new(10, 20); + let b = i16x2::new(30, 40); + let c = 50; + let r = (10 * 40) + c; + assert_eq!(super::__smlabt(transmute(a), transmute(b), c), r); + } + } + + fn smlatb() { + unsafe { + let a = i16x2::new(10, 20); + let b = i16x2::new(30, 40); + let c = 50; + let r = (20 * 30) + c; + assert_eq!(super::__smlabt(transmute(a), transmute(b), c), r); + } + } + + fn smlatt() { + unsafe { + let a = i16x2::new(10, 20); + let b = i16x2::new(30, 40); + let c = 50; + let r = (20 * 40) + c; + assert_eq!(super::__smlatt(transmute(a), transmute(b), c), r); + } + } + + fn smlawb() { + unsafe { + let a: i32 = 10; + let b = i16x2::new(30, 40); + let c: i32 = 50; + let r: i32 = ((a * 30) + (c << 16)) >> 16; + assert_eq!(super::__smlawb(a, transmute(b), c), r); + } + } + + fn smlawt() { + unsafe { + let a: i32 = 10; + let b = i16x2::new(30, 40); + let c: i32 = 50; + let r: i32 = ((a * 40) + (c << 16)) >> 16; + assert_eq!(super::__smlawt(a, transmute(b), c), r); + } + } +} |