Add some tests for SIMD

This commit is contained in:
Patrick Walton 2019-02-01 16:58:35 -08:00
parent f9aed5b077
commit 42bb7acddb
4 changed files with 270 additions and 55 deletions

67
simd/src/extras.rs Normal file
View File

@ -0,0 +1,67 @@
// pathfinder/simd/src/extras.rs
//
// Copyright © 2019 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::default::F32x4;
impl F32x4 {
// Accessors
#[inline]
pub fn x(self) -> f32 {
self[0]
}
#[inline]
pub fn y(self) -> f32 {
self[1]
}
#[inline]
pub fn z(self) -> f32 {
self[2]
}
#[inline]
pub fn w(self) -> f32 {
self[3]
}
// Mutators
#[inline]
pub fn set_x(&mut self, x: f32) {
self[0] = x
}
#[inline]
pub fn set_y(&mut self, y: f32) {
self[1] = y
}
#[inline]
pub fn set_z(&mut self, z: f32) {
self[2] = z
}
#[inline]
pub fn set_w(&mut self, w: f32) {
self[3] = w
}
// Comparisons
#[inline]
pub fn approx_eq(self, other: F32x4, epsilon: f32) -> bool {
(self - other)
.abs()
.packed_gt(F32x4::splat(epsilon))
.is_all_zeroes()
}
}

View File

@ -17,3 +17,7 @@ pub use crate::x86 as default;
pub mod scalar; pub mod scalar;
pub mod x86; pub mod x86;
mod extras;
#[cfg(test)]
mod test;

137
simd/src/test.rs Normal file
View File

@ -0,0 +1,137 @@
// pathfinder/simd/src/test.rs
//
// Copyright © 2019 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::default::{F32x4, I32x4, U32x4};
// F32x4
#[test]
fn test_f32x4_constructors() {
let a = F32x4::new(1.0, 2.0, 3.0, 4.0);
assert_eq!((a[0], a[1], a[2], a[3]), (1.0, 2.0, 3.0, 4.0));
let b = F32x4::splat(10.0);
assert_eq!(b, F32x4::new(10.0, 10.0, 10.0, 10.0));
}
#[test]
fn test_f32x4_accessors_and_mutators() {
let a = F32x4::new(5.0, 6.0, 7.0, 8.0);
assert_eq!((a.x(), a.y(), a.z(), a.w()), (5.0, 6.0, 7.0, 8.0));
let mut b = F32x4::new(10.0, 11.0, 12.0, 13.0);
b.set_x(20.0);
b.set_y(30.0);
b.set_z(40.0);
b.set_w(50.0);
assert_eq!(b, F32x4::new(20.0, 30.0, 40.0, 50.0));
}
#[test]
fn test_f32x4_basic_ops() {
let a = F32x4::new(1.0, 3.0, 5.0, 7.0);
let b = F32x4::new(2.0, 2.0, 6.0, 6.0);
assert_eq!(a.min(b), F32x4::new(1.0, 2.0, 5.0, 6.0));
assert_eq!(a.max(b), F32x4::new(2.0, 3.0, 6.0, 7.0));
let c = F32x4::new(-1.0, 1.0, -20.0, 3.0);
assert_eq!(c.abs(), F32x4::new(1.0, 1.0, 20.0, 3.0));
}
#[test]
fn test_f32x4_packed_comparisons() {
let a = F32x4::new(7.0, 3.0, 6.0, -2.0);
let b = F32x4::new(10.0, 3.0, 5.0, -2.0);
assert_eq!(a.packed_eq(b), U32x4::new(0, !0, 0, !0));
}
// TODO(pcwalton): This should test them all!
#[test]
fn test_f32x4_swizzles() {
let a = F32x4::new(1.0, 2.0, 3.0, 4.0);
assert_eq!(a.xxxx(), F32x4::splat(1.0));
assert_eq!(a.yyyy(), F32x4::splat(2.0));
assert_eq!(a.zzzz(), F32x4::splat(3.0));
assert_eq!(a.wwww(), F32x4::splat(4.0));
assert_eq!(a.xyxy(), F32x4::new(1.0, 2.0, 1.0, 2.0));
assert_eq!(a.yzzy(), F32x4::new(2.0, 3.0, 3.0, 2.0));
assert_eq!(a.wzyx(), F32x4::new(4.0, 3.0, 2.0, 1.0));
assert_eq!(a.ywzx(), F32x4::new(2.0, 4.0, 3.0, 1.0));
assert_eq!(a.wzwz(), F32x4::new(4.0, 3.0, 4.0, 3.0));
}
#[test]
fn test_f32x4_concatenations() {
let a = F32x4::new(4.0, 2.0, 6.0, -1.0);
let b = F32x4::new(10.0, -3.0, 15.0, 41.0);
assert_eq!(a.concat_xy_xy(b), F32x4::new( 4.0, 2.0, 10.0, -3.0));
assert_eq!(a.concat_xy_zw(b), F32x4::new( 4.0, 2.0, 15.0, 41.0));
assert_eq!(a.concat_zw_zw(b), F32x4::new( 6.0, -1.0, 15.0, 41.0));
assert_eq!(a.concat_wz_yx(b), F32x4::new(-1.0, 6.0, -3.0, 10.0));
}
#[test]
fn test_f32x4_arithmetic_overloads() {
let a = F32x4::new(4.0, -1.0, 6.0, -32.0);
let b = F32x4::new(0.5, 0.5, 10.0, 3.0);
let a_plus_b = F32x4::new(4.5, -0.5, 16.0, -29.0);
let a_minus_b = F32x4::new(3.5, -1.5, -4.0, -35.0);
let a_times_b = F32x4::new(2.0, -0.5, 60.0, -96.0);
assert_eq!(a + b, a_plus_b);
assert_eq!(a - b, a_minus_b);
assert_eq!(a * b, a_times_b);
let mut c = a;
c += b;
assert_eq!(c, a_plus_b);
c = a;
c -= b;
assert_eq!(c, a_minus_b);
c = a;
c *= b;
assert_eq!(c, a_times_b);
assert_eq!(-a, F32x4::new(-4.0, 1.0, -6.0, 32.0));
}
#[test]
fn test_f32x4_index_overloads() {
let mut a = F32x4::new(4.0, 1.0, -32.5, 75.0);
assert_eq!(a[2], -32.5);
a[3] = 300.0;
assert_eq!(a[3], 300.0);
a[0] *= 0.5;
assert_eq!(a[0], 2.0);
}
#[test]
fn test_f32x4_conversions() {
let a = F32x4::new(48.0, -4.0, 200.0, 7.0);
assert_eq!(a.to_i32x4(), I32x4::new(48, -4, 200, 7));
}
// I32x4
#[test]
fn test_i32x4_constructors() {
let a = I32x4::new(3, 58, 10, 4);
assert_eq!((a[0], a[1], a[2], a[3]), (3, 58, 10, 4));
let b = I32x4::splat(39);
assert_eq!(b, I32x4::new(39, 39, 39, 39));
}
#[test]
fn test_i32x4_basic_ops() {
let a = I32x4::new(6, 29, -40, 2 );
let b = I32x4::new(10, -5, 10, 46);
assert_eq!(a.min(b), I32x4::new(6, -5, -40, 2));
}
#[test]
fn test_i32x4_packed_comparisons() {
let a = I32x4::new( 59, 1, 5, 63 );
let b = I32x4::new(-59, 1, 5, 104);
assert_eq!(a.packed_eq(b), U32x4::new(0, !0, !0, 0));
}

View File

@ -35,51 +35,7 @@ impl F32x4 {
unsafe { F32x4(x86_64::_mm_set1_ps(x)) } unsafe { F32x4(x86_64::_mm_set1_ps(x)) }
} }
// Accessors // Basic operations
#[inline]
pub fn x(self) -> f32 {
self[0]
}
#[inline]
pub fn y(self) -> f32 {
self[1]
}
#[inline]
pub fn z(self) -> f32 {
self[2]
}
#[inline]
pub fn w(self) -> f32 {
self[3]
}
// Mutators
#[inline]
pub fn set_x(&mut self, x: f32) {
self[0] = x
}
#[inline]
pub fn set_y(&mut self, y: f32) {
self[1] = y
}
#[inline]
pub fn set_z(&mut self, z: f32) {
self[2] = z
}
#[inline]
pub fn set_w(&mut self, w: f32) {
self[3] = w
}
// Basic ops
#[inline] #[inline]
pub fn min(self, other: F32x4) -> F32x4 { pub fn min(self, other: F32x4) -> F32x4 {
@ -99,6 +55,8 @@ impl F32x4 {
} }
} }
// Packed comparisons
#[inline] #[inline]
pub fn packed_eq(self, other: F32x4) -> U32x4 { pub fn packed_eq(self, other: F32x4) -> U32x4 {
unsafe { unsafe {
@ -117,15 +75,9 @@ impl F32x4 {
} }
} }
#[inline] // Conversions
pub fn approx_eq(self, other: F32x4, epsilon: f32) -> bool {
(self - other)
.abs()
.packed_gt(F32x4::splat(epsilon))
.is_all_zeroes()
}
// Converts these packed floats to integers. /// Converts these packed floats to integers.
#[inline] #[inline]
pub fn to_i32x4(self) -> I32x4 { pub fn to_i32x4(self) -> I32x4 {
unsafe { I32x4(x86_64::_mm_cvtps_epi32(self.0)) } unsafe { I32x4(x86_64::_mm_cvtps_epi32(self.0)) }
@ -1425,6 +1377,7 @@ impl F32x4 {
} }
} }
#[inline]
pub fn concat_xy_zw(self, other: F32x4) -> F32x4 { pub fn concat_xy_zw(self, other: F32x4) -> F32x4 {
unsafe { unsafe {
let this = x86_64::_mm_castps_pd(self.0); let this = x86_64::_mm_castps_pd(self.0);
@ -1584,6 +1537,13 @@ impl I32x4 {
pub fn min(self, other: I32x4) -> I32x4 { pub fn min(self, other: I32x4) -> I32x4 {
unsafe { I32x4(x86_64::_mm_min_epi32(self.0, other.0)) } unsafe { I32x4(x86_64::_mm_min_epi32(self.0, other.0)) }
} }
// Packed comparisons
#[inline]
pub fn packed_eq(self, other: I32x4) -> U32x4 {
unsafe { U32x4(x86_64::_mm_cmpeq_epi32(self.0, other.0)) }
}
} }
impl Index<usize> for I32x4 { impl Index<usize> for I32x4 {
@ -1602,21 +1562,61 @@ impl Sub<I32x4> for I32x4 {
} }
} }
impl Debug for I32x4 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "<{}, {}, {}, {}>", self[0], self[1], self[2], self[3])
}
}
impl PartialEq for I32x4 {
#[inline]
fn eq(&self, other: &I32x4) -> bool {
self.packed_eq(*other).is_all_ones()
}
}
// 32-bit unsigned integers // 32-bit unsigned integers
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct U32x4(pub __m128i); pub struct U32x4(pub __m128i);
impl U32x4 { impl U32x4 {
// Constructors
#[inline] #[inline]
fn is_all_ones(&self) -> bool { pub fn new(a: u32, b: u32, c: u32, d: u32) -> U32x4 {
unsafe {
let vector = [a, b, c, d];
U32x4(x86_64::_mm_loadu_si128(vector.as_ptr() as *const __m128i))
}
}
// Basic operations
#[inline]
pub fn is_all_ones(self) -> bool {
unsafe { x86_64::_mm_test_all_ones(self.0) != 0 } unsafe { x86_64::_mm_test_all_ones(self.0) != 0 }
} }
#[inline] #[inline]
fn is_all_zeroes(&self) -> bool { pub fn is_all_zeroes(self) -> bool {
unsafe { x86_64::_mm_test_all_zeros(self.0, self.0) != 0 } unsafe { x86_64::_mm_test_all_zeros(self.0, self.0) != 0 }
} }
// Packed comparisons
#[inline]
pub fn packed_eq(self, other: U32x4) -> U32x4 {
unsafe { U32x4(x86_64::_mm_cmpeq_epi32(self.0, other.0)) }
}
}
impl Debug for U32x4 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "<{}, {}, {}, {}>", self[0], self[1], self[2], self[3])
}
} }
impl Index<usize> for U32x4 { impl Index<usize> for U32x4 {
@ -1627,6 +1627,13 @@ impl Index<usize> for U32x4 {
} }
} }
impl PartialEq for U32x4 {
#[inline]
fn eq(&self, other: &U32x4) -> bool {
self.packed_eq(*other).is_all_ones()
}
}
// 8-bit unsigned integers // 8-bit unsigned integers
#[derive(Clone, Copy)] #[derive(Clone, Copy)]