diff --git a/simd/src/extras.rs b/simd/src/extras.rs new file mode 100644 index 00000000..f738d852 --- /dev/null +++ b/simd/src/extras.rs @@ -0,0 +1,67 @@ +// pathfinder/simd/src/extras.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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() + } +} diff --git a/simd/src/lib.rs b/simd/src/lib.rs index 8f6d0b54..cfae3985 100644 --- a/simd/src/lib.rs +++ b/simd/src/lib.rs @@ -17,3 +17,7 @@ pub use crate::x86 as default; pub mod scalar; pub mod x86; +mod extras; + +#[cfg(test)] +mod test; diff --git a/simd/src/test.rs b/simd/src/test.rs new file mode 100644 index 00000000..6186ef93 --- /dev/null +++ b/simd/src/test.rs @@ -0,0 +1,137 @@ +// pathfinder/simd/src/test.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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)); +} diff --git a/simd/src/x86.rs b/simd/src/x86.rs index 87a52acd..cfd130c3 100644 --- a/simd/src/x86.rs +++ b/simd/src/x86.rs @@ -35,51 +35,7 @@ impl F32x4 { unsafe { F32x4(x86_64::_mm_set1_ps(x)) } } - // 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 - } - - // Basic ops + // Basic operations #[inline] pub fn min(self, other: F32x4) -> F32x4 { @@ -99,6 +55,8 @@ impl F32x4 { } } + // Packed comparisons + #[inline] pub fn packed_eq(self, other: F32x4) -> U32x4 { unsafe { @@ -117,15 +75,9 @@ impl F32x4 { } } - #[inline] - pub fn approx_eq(self, other: F32x4, epsilon: f32) -> bool { - (self - other) - .abs() - .packed_gt(F32x4::splat(epsilon)) - .is_all_zeroes() - } + // Conversions - // Converts these packed floats to integers. + /// Converts these packed floats to integers. #[inline] pub fn to_i32x4(self) -> I32x4 { 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 { unsafe { let this = x86_64::_mm_castps_pd(self.0); @@ -1584,6 +1537,13 @@ impl I32x4 { pub fn min(self, other: I32x4) -> I32x4 { 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 for I32x4 { @@ -1602,21 +1562,61 @@ impl Sub 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 #[derive(Clone, Copy)] pub struct U32x4(pub __m128i); impl U32x4 { + // Constructors + #[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 } } #[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 } } + + // 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 for U32x4 { @@ -1627,6 +1627,13 @@ impl Index for U32x4 { } } +impl PartialEq for U32x4 { + #[inline] + fn eq(&self, other: &U32x4) -> bool { + self.packed_eq(*other).is_all_ones() + } +} + // 8-bit unsigned integers #[derive(Clone, Copy)]