Paolo Teti e0504ce54a Add few ARM DSP Intrinsics (#529)
* Add few ARM DSP Intrinsics

- Signed saturating add/sub
- Saturating four 8-bit integer add/sub
- Saturating two 8-bit integer add/sub

The intent is mainly to setup the module and to add all
the rest in the future.

Listed intrinsics are available on Cortex-M too (+dsp is required
on some model except for M4).

* Arm DSP: rebase and remove portable vector types

Rebase everything on top of master since the portable vector types
have been removed.
2018-07-20 11:54:52 -05:00

176 lines
4.9 KiB
Rust

//! ARM DSP Intrinsics.
#[cfg(test)]
use stdsimd_test::assert_instr;
types! {
/// ARM-specific 32-bit wide vector of four packed `i8`.
pub struct int8x4_t(i8, i8, i8, i8);
/// ARM-specific 32-bit wide vector of four packed `u8`.
pub struct uint8x4_t(u8, u8, u8, u8);
/// 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 "C" {
#[cfg_attr(target_arch = "arm", link_name = "llvm.arm.qadd")]
fn arm_qadd(a: i32, b: i32) -> i32;
#[cfg_attr(target_arch = "arm", link_name = "llvm.arm.qsub")]
fn arm_qsub(a: i32, b: i32) -> i32;
#[cfg_attr(target_arch = "arm", link_name = "llvm.arm.qadd8")]
fn arm_qadd8(a: i32, b: i32) -> i32;
#[cfg_attr(target_arch = "arm", link_name = "llvm.arm.qsub8")]
fn arm_qsub8(a: i32, b: i32) -> i32;
#[cfg_attr(target_arch = "arm", link_name = "llvm.arm.qadd16")]
fn arm_qadd16(a: i32, b: i32) -> i32;
#[cfg_attr(target_arch = "arm", link_name = "llvm.arm.qsub16")]
fn arm_qsub16(a: i32, b: i32) -> i32;
}
/// Signed saturating addition
///
/// Returns the 32-bit saturating signed equivalent of a + b.
#[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.
#[inline]
#[cfg_attr(test, assert_instr(qsub))]
pub unsafe fn qsub(a: i32, b: i32) -> i32 {
arm_qsub(a, b)
}
/// Saturating four 8-bit integer additions
///
/// Returns the 8-bit signed equivalent of
///
/// res[0] = a[0] + b[0]
/// res[1] = a[1] + b[1]
/// res[2] = a[2] + b[2]
/// res[3] = a[3] + b[3]
#[inline]
#[cfg_attr(test, assert_instr(qadd8))]
pub unsafe fn qadd8(a: int8x4_t, b: int8x4_t) -> int8x4_t {
::mem::transmute(arm_qadd8(::mem::transmute(a), ::mem::transmute(b)))
}
/// Saturating two 8-bit integer subtraction
///
/// Returns the 8-bit signed equivalent of
///
/// res[0] = a[0] - b[0]
/// res[1] = a[1] - b[1]
/// res[2] = a[2] - b[2]
/// res[3] = a[3] - b[3]
#[inline]
#[cfg_attr(test, assert_instr(qsub8))]
pub unsafe fn qsub8(a: int8x4_t, b: int8x4_t) -> int8x4_t {
::mem::transmute(arm_qsub8(::mem::transmute(a), ::mem::transmute(b)))
}
/// Saturating two 16-bit integer subtraction
///
/// Returns the 16-bit signed equivalent of
///
/// res[0] = a[0] - b[0]
/// res[1] = a[1] - b[1]
#[inline]
#[cfg_attr(test, assert_instr(qsub16))]
pub unsafe fn qsub16(a: int16x2_t, b: int16x2_t) -> int16x2_t {
::mem::transmute(arm_qsub16(::mem::transmute(a), ::mem::transmute(b)))
}
/// Saturating two 16-bit integer additions
///
/// Returns the 16-bit signed equivalent of
///
/// res[0] = a[0] + b[0]
/// res[1] = a[1] + b[1]
#[inline]
#[cfg_attr(test, assert_instr(qadd16))]
pub unsafe fn qadd16(a: int16x2_t, b: int16x2_t) -> int16x2_t {
::mem::transmute(arm_qadd16(::mem::transmute(a), ::mem::transmute(b)))
}
#[cfg(test)]
mod tests {
use coresimd::arm::*;
use coresimd::simd::*;
use std::mem;
use stdsimd_test::simd_test;
#[test]
fn qadd() {
unsafe {
assert_eq!(dsp::qadd(-10, 60), 50);
assert_eq!(dsp::qadd(::std::i32::MAX, 10), ::std::i32::MAX);
assert_eq!(dsp::qadd(::std::i32::MIN, -10), ::std::i32::MIN);
}
}
#[test]
fn qsub() {
unsafe {
assert_eq!(dsp::qsub(10, 60), -50);
assert_eq!(dsp::qsub(::std::i32::MAX, -10), ::std::i32::MAX);
assert_eq!(dsp::qsub(::std::i32::MIN, 10), ::std::i32::MIN);
}
}
#[test]
fn qadd8() {
unsafe {
let a = i8x4::new(1, 2, 3, ::std::i8::MAX);
let b = i8x4::new(2, -1, 0, 1);
let c = i8x4::new(3, 1, 3, ::std::i8::MAX);
let r: i8x4 = ::mem::transmute(dsp::qadd8(::mem::transmute(a), ::mem::transmute(b)));
assert_eq!(r, c);
}
}
#[test]
fn qsub8() {
unsafe {
let a = i8x4::new(1, 2, 3, ::std::i8::MIN);
let b = i8x4::new(2, -1, 0, 1);
let c = i8x4::new(-1, 3, 3, ::std::i8::MIN);
let r: i8x4 = ::mem::transmute(dsp::qsub8(::mem::transmute(a),::mem::transmute(b)));
assert_eq!(r, c);
}
}
#[test]
fn qadd16() {
unsafe {
let a = i16x2::new(1, 2);
let b = i16x2::new(2, -1);
let c = i16x2::new(3, 1);
let r: i16x2 = ::mem::transmute(dsp::qadd16(::mem::transmute(a),::mem::transmute(b)));
assert_eq!(r, c);
}
}
#[test]
fn qsub16() {
unsafe {
let a = i16x2::new(10, 20);
let b = i16x2::new(20, -10);
let c = i16x2::new(-10, 30);
let r: i16x2 = ::mem::transmute(dsp::qsub16(::mem::transmute(a), ::mem::transmute(b)));
assert_eq!(r, c);
}
}
}