| //! # 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); |
| } |
| } |
| } |