summaryrefslogtreecommitdiffstats
path: root/library/stdarch/crates/core_arch/src/arm/dsp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/stdarch/crates/core_arch/src/arm/dsp.rs')
-rw-r--r--library/stdarch/crates/core_arch/src/arm/dsp.rs384
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);
+ }
+ }
+}