Skip to content

Commit c44a608

Browse files
Merge pull request #278 from rust-lang/feature/simd-traits
Move element-specific functions to traits
2 parents 939914e + 62d3b2e commit c44a608

File tree

10 files changed

+777
-681
lines changed

10 files changed

+777
-681
lines changed

crates/core_simd/src/elements.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
mod float;
2+
mod int;
3+
mod uint;
4+
5+
mod sealed {
6+
pub trait Sealed {}
7+
}
8+
9+
pub use float::*;
10+
pub use int::*;
11+
pub use uint::*;
+344
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
use super::sealed::Sealed;
2+
use crate::simd::{
3+
intrinsics, LaneCount, Mask, Simd, SimdElement, SimdPartialEq, SimdPartialOrd,
4+
SupportedLaneCount,
5+
};
6+
7+
/// Operations on SIMD vectors of floats.
8+
pub trait SimdFloat: Copy + Sealed {
9+
/// Mask type used for manipulating this SIMD vector type.
10+
type Mask;
11+
12+
/// Scalar type contained by this SIMD vector type.
13+
type Scalar;
14+
15+
/// Bit representation of this SIMD vector type.
16+
type Bits;
17+
18+
/// Raw transmutation to an unsigned integer vector type with the
19+
/// same size and number of lanes.
20+
#[must_use = "method returns a new vector and does not mutate the original value"]
21+
fn to_bits(self) -> Self::Bits;
22+
23+
/// Raw transmutation from an unsigned integer vector type with the
24+
/// same size and number of lanes.
25+
#[must_use = "method returns a new vector and does not mutate the original value"]
26+
fn from_bits(bits: Self::Bits) -> Self;
27+
28+
/// Produces a vector where every lane has the absolute value of the
29+
/// equivalently-indexed lane in `self`.
30+
#[must_use = "method returns a new vector and does not mutate the original value"]
31+
fn abs(self) -> Self;
32+
33+
/// Takes the reciprocal (inverse) of each lane, `1/x`.
34+
#[must_use = "method returns a new vector and does not mutate the original value"]
35+
fn recip(self) -> Self;
36+
37+
/// Converts each lane from radians to degrees.
38+
#[must_use = "method returns a new vector and does not mutate the original value"]
39+
fn to_degrees(self) -> Self;
40+
41+
/// Converts each lane from degrees to radians.
42+
#[must_use = "method returns a new vector and does not mutate the original value"]
43+
fn to_radians(self) -> Self;
44+
45+
/// Returns true for each lane if it has a positive sign, including
46+
/// `+0.0`, `NaN`s with positive sign bit and positive infinity.
47+
#[must_use = "method returns a new mask and does not mutate the original value"]
48+
fn is_sign_positive(self) -> Self::Mask;
49+
50+
/// Returns true for each lane if it has a negative sign, including
51+
/// `-0.0`, `NaN`s with negative sign bit and negative infinity.
52+
#[must_use = "method returns a new mask and does not mutate the original value"]
53+
fn is_sign_negative(self) -> Self::Mask;
54+
55+
/// Returns true for each lane if its value is `NaN`.
56+
#[must_use = "method returns a new mask and does not mutate the original value"]
57+
fn is_nan(self) -> Self::Mask;
58+
59+
/// Returns true for each lane if its value is positive infinity or negative infinity.
60+
#[must_use = "method returns a new mask and does not mutate the original value"]
61+
fn is_infinite(self) -> Self::Mask;
62+
63+
/// Returns true for each lane if its value is neither infinite nor `NaN`.
64+
#[must_use = "method returns a new mask and does not mutate the original value"]
65+
fn is_finite(self) -> Self::Mask;
66+
67+
/// Returns true for each lane if its value is subnormal.
68+
#[must_use = "method returns a new mask and does not mutate the original value"]
69+
fn is_subnormal(self) -> Self::Mask;
70+
71+
/// Returns true for each lane if its value is neither zero, infinite,
72+
/// subnormal, nor `NaN`.
73+
#[must_use = "method returns a new mask and does not mutate the original value"]
74+
fn is_normal(self) -> Self::Mask;
75+
76+
/// Replaces each lane with a number that represents its sign.
77+
///
78+
/// * `1.0` if the number is positive, `+0.0`, or `INFINITY`
79+
/// * `-1.0` if the number is negative, `-0.0`, or `NEG_INFINITY`
80+
/// * `NAN` if the number is `NAN`
81+
#[must_use = "method returns a new vector and does not mutate the original value"]
82+
fn signum(self) -> Self;
83+
84+
/// Returns each lane with the magnitude of `self` and the sign of `sign`.
85+
///
86+
/// For any lane containing a `NAN`, a `NAN` with the sign of `sign` is returned.
87+
#[must_use = "method returns a new vector and does not mutate the original value"]
88+
fn copysign(self, sign: Self) -> Self;
89+
90+
/// Returns the minimum of each lane.
91+
///
92+
/// If one of the values is `NAN`, then the other value is returned.
93+
#[must_use = "method returns a new vector and does not mutate the original value"]
94+
fn simd_min(self, other: Self) -> Self;
95+
96+
/// Returns the maximum of each lane.
97+
///
98+
/// If one of the values is `NAN`, then the other value is returned.
99+
#[must_use = "method returns a new vector and does not mutate the original value"]
100+
fn simd_max(self, other: Self) -> Self;
101+
102+
/// Restrict each lane to a certain interval unless it is NaN.
103+
///
104+
/// For each lane in `self`, returns the corresponding lane in `max` if the lane is
105+
/// greater than `max`, and the corresponding lane in `min` if the lane is less
106+
/// than `min`. Otherwise returns the lane in `self`.
107+
#[must_use = "method returns a new vector and does not mutate the original value"]
108+
fn simd_clamp(self, min: Self, max: Self) -> Self;
109+
110+
/// Returns the sum of the lanes of the vector.
111+
///
112+
/// # Examples
113+
///
114+
/// ```
115+
/// # #![feature(portable_simd)]
116+
/// # use core::simd::f32x2;
117+
/// let v = f32x2::from_array([1., 2.]);
118+
/// assert_eq!(v.reduce_sum(), 3.);
119+
/// ```
120+
fn reduce_sum(self) -> Self::Scalar;
121+
122+
/// Reducing multiply. Returns the product of the lanes of the vector.
123+
///
124+
/// # Examples
125+
///
126+
/// ```
127+
/// # #![feature(portable_simd)]
128+
/// # use core::simd::f32x2;
129+
/// let v = f32x2::from_array([3., 4.]);
130+
/// assert_eq!(v.reduce_product(), 12.);
131+
/// ```
132+
fn reduce_product(self) -> Self::Scalar;
133+
134+
/// Returns the maximum lane in the vector.
135+
///
136+
/// Returns values based on equality, so a vector containing both `0.` and `-0.` may
137+
/// return either.
138+
///
139+
/// This function will not return `NaN` unless all lanes are `NaN`.
140+
///
141+
/// # Examples
142+
///
143+
/// ```
144+
/// # #![feature(portable_simd)]
145+
/// # use core::simd::f32x2;
146+
/// let v = f32x2::from_array([1., 2.]);
147+
/// assert_eq!(v.reduce_max(), 2.);
148+
///
149+
/// // NaN values are skipped...
150+
/// let v = f32x2::from_array([1., f32::NAN]);
151+
/// assert_eq!(v.reduce_max(), 1.);
152+
///
153+
/// // ...unless all values are NaN
154+
/// let v = f32x2::from_array([f32::NAN, f32::NAN]);
155+
/// assert!(v.reduce_max().is_nan());
156+
/// ```
157+
fn reduce_max(self) -> Self::Scalar;
158+
159+
/// Returns the minimum lane in the vector.
160+
///
161+
/// Returns values based on equality, so a vector containing both `0.` and `-0.` may
162+
/// return either.
163+
///
164+
/// This function will not return `NaN` unless all lanes are `NaN`.
165+
///
166+
/// # Examples
167+
///
168+
/// ```
169+
/// # #![feature(portable_simd)]
170+
/// # use core::simd::f32x2;
171+
/// let v = f32x2::from_array([3., 7.]);
172+
/// assert_eq!(v.reduce_min(), 3.);
173+
///
174+
/// // NaN values are skipped...
175+
/// let v = f32x2::from_array([1., f32::NAN]);
176+
/// assert_eq!(v.reduce_min(), 1.);
177+
///
178+
/// // ...unless all values are NaN
179+
/// let v = f32x2::from_array([f32::NAN, f32::NAN]);
180+
/// assert!(v.reduce_min().is_nan());
181+
/// ```
182+
fn reduce_min(self) -> Self::Scalar;
183+
}
184+
185+
macro_rules! impl_trait {
186+
{ $($ty:ty { bits: $bits_ty:ty, mask: $mask_ty:ty }),* } => {
187+
$(
188+
impl<const LANES: usize> Sealed for Simd<$ty, LANES>
189+
where
190+
LaneCount<LANES>: SupportedLaneCount,
191+
{
192+
}
193+
194+
impl<const LANES: usize> SimdFloat for Simd<$ty, LANES>
195+
where
196+
LaneCount<LANES>: SupportedLaneCount,
197+
{
198+
type Mask = Mask<<$mask_ty as SimdElement>::Mask, LANES>;
199+
type Scalar = $ty;
200+
type Bits = Simd<$bits_ty, LANES>;
201+
202+
#[inline]
203+
fn to_bits(self) -> Simd<$bits_ty, LANES> {
204+
assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Self::Bits>());
205+
unsafe { core::mem::transmute_copy(&self) }
206+
}
207+
208+
#[inline]
209+
fn from_bits(bits: Simd<$bits_ty, LANES>) -> Self {
210+
assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Self::Bits>());
211+
unsafe { core::mem::transmute_copy(&bits) }
212+
}
213+
214+
#[inline]
215+
fn abs(self) -> Self {
216+
unsafe { intrinsics::simd_fabs(self) }
217+
}
218+
219+
#[inline]
220+
fn recip(self) -> Self {
221+
Self::splat(1.0) / self
222+
}
223+
224+
#[inline]
225+
fn to_degrees(self) -> Self {
226+
// to_degrees uses a special constant for better precision, so extract that constant
227+
self * Self::splat(Self::Scalar::to_degrees(1.))
228+
}
229+
230+
#[inline]
231+
fn to_radians(self) -> Self {
232+
self * Self::splat(Self::Scalar::to_radians(1.))
233+
}
234+
235+
#[inline]
236+
fn is_sign_positive(self) -> Self::Mask {
237+
!self.is_sign_negative()
238+
}
239+
240+
#[inline]
241+
fn is_sign_negative(self) -> Self::Mask {
242+
let sign_bits = self.to_bits() & Simd::splat((!0 >> 1) + 1);
243+
sign_bits.simd_gt(Simd::splat(0))
244+
}
245+
246+
#[inline]
247+
fn is_nan(self) -> Self::Mask {
248+
self.simd_ne(self)
249+
}
250+
251+
#[inline]
252+
fn is_infinite(self) -> Self::Mask {
253+
self.abs().simd_eq(Self::splat(Self::Scalar::INFINITY))
254+
}
255+
256+
#[inline]
257+
fn is_finite(self) -> Self::Mask {
258+
self.abs().simd_lt(Self::splat(Self::Scalar::INFINITY))
259+
}
260+
261+
#[inline]
262+
fn is_subnormal(self) -> Self::Mask {
263+
self.abs().simd_ne(Self::splat(0.0)) & (self.to_bits() & Self::splat(Self::Scalar::INFINITY).to_bits()).simd_eq(Simd::splat(0))
264+
}
265+
266+
#[inline]
267+
#[must_use = "method returns a new mask and does not mutate the original value"]
268+
fn is_normal(self) -> Self::Mask {
269+
!(self.abs().simd_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite())
270+
}
271+
272+
#[inline]
273+
fn signum(self) -> Self {
274+
self.is_nan().select(Self::splat(Self::Scalar::NAN), Self::splat(1.0).copysign(self))
275+
}
276+
277+
#[inline]
278+
fn copysign(self, sign: Self) -> Self {
279+
let sign_bit = sign.to_bits() & Self::splat(-0.).to_bits();
280+
let magnitude = self.to_bits() & !Self::splat(-0.).to_bits();
281+
Self::from_bits(sign_bit | magnitude)
282+
}
283+
284+
#[inline]
285+
fn simd_min(self, other: Self) -> Self {
286+
unsafe { intrinsics::simd_fmin(self, other) }
287+
}
288+
289+
#[inline]
290+
fn simd_max(self, other: Self) -> Self {
291+
unsafe { intrinsics::simd_fmax(self, other) }
292+
}
293+
294+
#[inline]
295+
fn simd_clamp(self, min: Self, max: Self) -> Self {
296+
assert!(
297+
min.simd_le(max).all(),
298+
"each lane in `min` must be less than or equal to the corresponding lane in `max`",
299+
);
300+
let mut x = self;
301+
x = x.simd_lt(min).select(min, x);
302+
x = x.simd_gt(max).select(max, x);
303+
x
304+
}
305+
306+
#[inline]
307+
fn reduce_sum(self) -> Self::Scalar {
308+
// LLVM sum is inaccurate on i586
309+
if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
310+
self.as_array().iter().sum()
311+
} else {
312+
// Safety: `self` is a float vector
313+
unsafe { intrinsics::simd_reduce_add_ordered(self, 0.) }
314+
}
315+
}
316+
317+
#[inline]
318+
fn reduce_product(self) -> Self::Scalar {
319+
// LLVM product is inaccurate on i586
320+
if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
321+
self.as_array().iter().product()
322+
} else {
323+
// Safety: `self` is a float vector
324+
unsafe { intrinsics::simd_reduce_mul_ordered(self, 1.) }
325+
}
326+
}
327+
328+
#[inline]
329+
fn reduce_max(self) -> Self::Scalar {
330+
// Safety: `self` is a float vector
331+
unsafe { intrinsics::simd_reduce_max(self) }
332+
}
333+
334+
#[inline]
335+
fn reduce_min(self) -> Self::Scalar {
336+
// Safety: `self` is a float vector
337+
unsafe { intrinsics::simd_reduce_min(self) }
338+
}
339+
}
340+
)*
341+
}
342+
}
343+
344+
impl_trait! { f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } }

0 commit comments

Comments
 (0)