wide/lib.rs
1#![no_std]
2#![allow(non_camel_case_types)]
3#![warn(clippy::doc_markdown)]
4#![warn(clippy::missing_inline_in_public_items)]
5#![allow(clippy::eq_op)]
6#![allow(clippy::excessive_precision)]
7#![allow(clippy::let_and_return)]
8#![allow(clippy::unusual_byte_groupings)]
9#![allow(clippy::misrefactored_assign_op)]
10#![allow(clippy::approx_constant)]
11#![forbid(missing_docs)]
12
13//! A crate to help you go wide.
14//!
15//! This crate provides SIMD-compatible data types.
16//!
17//! When possible, explicit SIMD is used with all the math operations here. As a
18//! fallback, the fact that all the lengths of a fixed length array are doing
19//! the same thing will often make LLVM notice that it should use SIMD
20//! instructions to complete the task. In the worst case, the code just becomes
21//! totally scalar (though the math is still correct, at least).
22//!
23//! # Masks
24//!
25//! SIMD vector masks are per-element booleans, and are represented by SIMD
26//! vectors where each element's value in bits is either all zeros (`false`) or
27//! all ones (`true`).
28//!
29//! SIMD versions of functions that regularly return `bool` return masks. For
30//! example, [`f32::is_sign_positive`] returns `bool`, and
31//! [`f32x4::is_sign_positive`] returns an [`f32x4`] that represents a mask.
32//! The [`select`] method can be used to perform a per-element `if` statement
33//! over a mask. For example, for this simple scalar code:
34//!
35//! ```
36//! let x = 1.0_f32;
37//!
38//! let result = if x.is_sign_positive() {
39//! 5.0
40//! } else {
41//! 3.0
42//! };
43//!
44//! assert_eq!(result, 5.0);
45//! ```
46//!
47//! This is the SIMD version:
48//!
49//! ```
50//! # use wide::f32x4;
51//! #
52//! let x = f32x4::new([1.0, -1.0, -1.0, 1.0]);
53//!
54//! let result = x.is_sign_positive().select(
55//! f32x4::splat(5.0),
56//! f32x4::splat(3.0),
57//! );
58//!
59//! assert_eq!(result, f32x4::new([5.0, 3.0, 3.0, 5.0]));
60//! ```
61//!
62//! To select from two values of a downstream type, implement the [`Select`]
63//! trait.
64//!
65//! # Shuffling
66//!
67//! Shuffling, also known as swizzling, creates a new SIMD vector by selecting
68//! elements from one or more input vectors according to a set of indices.
69//!
70//! Single-input shuffling is performed using the [`shuffle`] method and its
71//! variants. Multi-input shuffling is performed by placing the input vectors in
72//! an array and calling the corresponding methods from [`ShuffleExt`].
73//!
74//! Currently, `wide` only supports runtime-index based shuffling:
75//!
76//! ```
77//! use wide::{f32x4, ShuffleExt, u32x4};
78//!
79//! let simd_a = f32x4::new([0.0, 1.0, 2.0, 3.0]);
80//! let simd_b = f32x4::new([100.0, 101.0, 102.0, 103.0]);
81//!
82//! let reverse = simd_a.shuffle(u32x4::new([3, 2, 1, 0]));
83//!
84//! assert_eq!(reverse, f32x4::new([3.0, 2.0, 1.0, 0.0]));
85//!
86//! // Here, indices `0..4` map to `simd_a`, and indices `4..8` map to `simd_b`
87//! let from_two_vectors = [simd_a, simd_b].shuffle(u32x4::new([2, 3, 4, 5]));
88//!
89//! assert_eq!(from_two_vectors, f32x4::new([2.0, 3.0, 100.0, 101.0]));
90//! ```
91//!
92//! # NaN bit patterns
93//!
94//! Operations on SIMD vectors of floats do not make any guarantees about the
95//! specific bit-patterns of output NaN values (meaning the sign bit,
96//! quiet/signaling bit, and payload). This is unlike standard library
97//! operations on float primitives, which do define rules for what NaN bit
98//! patterns are returned.
99//!
100//! The reason for this is that enforcing guarantees would add substantial
101//! overhead to operations, and is generally not worth it.
102//!
103//! # Wrapping semantics
104//!
105//! SIMD vectors of integers treat operators as wrapping, as if [`Wrapping<T>`]
106//! was used. Thus, SIMD vectors do not implement `wrapping_*` functions,
107//! because that is the default behavior. This means there are no overflow
108//! checks, even in debug builds.
109//!
110//! The reason for this is that for most applications where SIMD is appropriate,
111//! it is "not a bug" to wrap, and even debug builds are unlikely to tolerate
112//! the loss of performance.
113//!
114//! This "no panicking" approach extends to more than just integer overflows. It
115//! is also true for things like [`f32x4::clamp`]. Even though [`f32::clamp`]
116//! panics for invalid inputs, the SIMD version does not, because it may be used
117//! with per-element branching, where invalid elements get discarded later by
118//! [`select`].
119//!
120//! # Casting
121//!
122//! The SIMD types implement the [`bytemuck::Pod`] trait, which means that it
123//! is possible to do bitwise casts between SIMD types of the same size with
124//! the [`bytemuck::cast()`] function and others. `bytemuck` is re-exported by
125//! this crate for convenience.
126//!
127//! This typically does not have much, if any, runtime overhead in optimized
128//! builds.
129//!
130//! # Feature flags
131//!
132//! * `std`: This causes the feature to link to `std`.
133//! * Currently this just improves the performance of `sqrt` when an explicit
134//! SIMD `sqrt` isn't available.
135//!
136//! [`select`]: f32x4::select
137//! [`shuffle`]: f32x4::shuffle
138//! [`Wrapping<T>`]: core::num::Wrapping
139
140// Note(Lokathor): Due to standard library magic, the std-only methods for f32
141// and f64 will automatically be available simply by declaring this.
142#[cfg(feature = "std")]
143extern crate std;
144
145// TODO
146// Add/Sub/Mul/Div with constant
147// Shuffle left/right/by index
148
149use core::ops::*;
150
151#[allow(unused_imports)]
152#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
153use safe_arch::*;
154
155use bytemuck::*;
156
157// Re-export so that users don't need to add a bytemuck dependency of their own
158pub use bytemuck;
159
160#[macro_use]
161mod simd;
162#[macro_use]
163mod simd_float;
164#[macro_use]
165mod simd_int;
166#[macro_use]
167mod simd_uint;
168
169macro_rules! pick {
170 ($(if #[cfg($($test:meta),*)] {
171 $($if_tokens:tt)*
172 })else+ else {
173 $($else_tokens:tt)*
174 }) => {
175 pick!{
176 @__forests [ ] ;
177 $( [ {$($test),*} {$($if_tokens)*} ], )*
178 [ { } {$($else_tokens)*} ],
179 }
180 };
181 (if #[cfg($($if_meta:meta),*)] {
182 $($if_tokens:tt)*
183 } $(else if #[cfg($($else_meta:meta),*)] {
184 $($else_tokens:tt)*
185 })*) => {
186 pick!{
187 @__forests [ ] ;
188 [ {$($if_meta),*} {$($if_tokens)*} ],
189 $( [ {$($else_meta),*} {$($else_tokens)*} ], )*
190 }
191 };
192 (@__forests [$($not:meta,)*];) => {
193 /* halt expansion */
194 };
195 (@__forests [$($not:meta,)*]; [{$($m:meta),*} {$($tokens:tt)*}], $($rest:tt)*) => {
196 #[cfg(all( $($m,)* not(any($($not),*)) ))]
197 pick!{ @__identity $($tokens)* }
198 pick!{ @__forests [ $($not,)* $($m,)* ] ; $($rest)* }
199 };
200 (@__identity $($tokens:tt)*) => {
201 $($tokens)*
202 };
203}
204
205// TODO: make these generic over `mul_add`? Worth it?
206
207macro_rules! polynomial_2 {
208 ($x:expr, $c0:expr, $c1:expr, $c2:expr $(,)?) => {{
209 let x = $x;
210 let x2 = x * x;
211 x2.mul_add($c2, x.mul_add($c1, $c0))
212 }};
213}
214
215macro_rules! polynomial_3 {
216 ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr $(,)?) => {{
217 let x = $x;
218 let x2 = x * x;
219 $c3.mul_add(x, $c2).mul_add(x2, $c1.mul_add(x, $c0))
220 }};
221}
222
223macro_rules! polynomial_4 {
224 ($x:expr, $c0:expr, $c1:expr, $c2:expr ,$c3:expr, $c4:expr $(,)?) => {{
225 let x = $x;
226 let x2 = x * x;
227 let x4 = x2 * x2;
228 $c3.mul_add(x, $c2).mul_add(x2, $c1.mul_add(x, $c0)) + $c4 * x4
229 }};
230}
231
232macro_rules! polynomial_5 {
233 ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr $(,)?) => {{
234 let x = $x;
235 let x2 = x * x;
236 let x4 = x2 * x2;
237 $c3
238 .mul_add(x, $c2)
239 .mul_add(x2, $c5.mul_add(x, $c4).mul_add(x4, $c1.mul_add(x, $c0)))
240 }};
241}
242
243macro_rules! polynomial_5n {
244 ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr $(,)?) => {{
245 let x = $x;
246 let x2 = x * x;
247 let x4 = x2 * x2;
248 x2.mul_add(x.mul_add($c3, $c2), (x4.mul_add($c4 + x, x.mul_add($c1, $c0))))
249 }};
250}
251
252macro_rules! polynomial_6 {
253 ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr ,$c6:expr $(,)?) => {{
254 let x = $x;
255 let x2 = x * x;
256 let x4 = x2 * x2;
257 x4.mul_add(
258 x2.mul_add($c6, x.mul_add($c5, $c4)),
259 x2.mul_add(x.mul_add($c3, $c2), x.mul_add($c1, $c0)),
260 )
261 }};
262}
263
264macro_rules! polynomial_6n {
265 ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr $(,)?) => {{
266 let x = $x;
267 let x2 = x * x;
268 let x4 = x2 * x2;
269 x4.mul_add(
270 x.mul_add($c5, x2 + $c4),
271 x2.mul_add(x.mul_add($c3, $c2), x.mul_add($c1, $c0)),
272 )
273 }};
274}
275
276macro_rules! polynomial_7 {
277 ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr, $c6:expr, $c7:expr $(,)?) => {{
278 let x = $x;
279 let x2 = x * x;
280 let x4 = x2 * x2;
281 x4.mul_add(
282 x2.mul_add(x.mul_add($c7, $c6), x.mul_add($c5, $c4)),
283 x2.mul_add(x.mul_add($c3, $c2), x.mul_add($c1, $c0)),
284 )
285 }};
286}
287
288macro_rules! polynomial_8 {
289 ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr, $c6:expr, $c7:expr, $c8:expr $(,)?) => {{
290 let x = $x;
291 let x2 = x * x;
292 let x4 = x2 * x2;
293 let x8 = x4 * x4;
294 x4.mul_add(
295 x2.mul_add($c7.mul_add(x, $c6), x.mul_add($c5, $c4)),
296 x8.mul_add($c8, x2.mul_add(x.mul_add($c3, $c2), x.mul_add($c1, $c0))),
297 )
298 }};
299}
300
301macro_rules! polynomial_13 {
302 // calculates polynomial c13*x^13 + c12*x^12 + ... + c1*x + c0
303 ($x:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr,$c6:expr, $c7:expr, $c8:expr,$c9:expr, $c10:expr, $c11:expr, $c12:expr, $c13:expr $(,)?) => {{
304 let x = $x;
305 let x2 = x * x;
306 let x4 = x2 * x2;
307 let x8 = x4 * x4;
308 x8.mul_add(
309 x4.mul_add(
310 x.mul_add($c13, $c12),
311 x2.mul_add(x.mul_add($c11, $c10), x.mul_add($c9, $c8)),
312 ),
313 x4.mul_add(
314 x2.mul_add(x.mul_add($c7, $c6), x.mul_add($c5, $c4)),
315 x2.mul_add(x.mul_add($c3, $c2), x),
316 ),
317 )
318 }};
319}
320
321mod f32x16_;
322pub use f32x16_::*;
323
324mod f32x8_;
325pub use f32x8_::*;
326
327mod f32x4_;
328pub use f32x4_::*;
329
330mod f64x8_;
331pub use f64x8_::*;
332
333mod f64x4_;
334pub use f64x4_::*;
335
336mod f64x2_;
337pub use f64x2_::*;
338
339mod i8x16_;
340pub use i8x16_::*;
341
342mod i16x16_;
343pub use i16x16_::*;
344
345mod i16x32_;
346pub use i16x32_::*;
347
348mod i8x32_;
349pub use i8x32_::*;
350
351mod i8x64_;
352pub use i8x64_::*;
353
354mod i16x8_;
355pub use i16x8_::*;
356
357mod i32x4_;
358pub use i32x4_::*;
359
360mod i32x8_;
361pub use i32x8_::*;
362
363mod i32x16_;
364pub use i32x16_::*;
365
366mod i64x2_;
367pub use i64x2_::*;
368
369mod i64x4_;
370pub use i64x4_::*;
371
372mod i64x8_;
373pub use i64x8_::*;
374
375mod u8x16_;
376pub use u8x16_::*;
377
378mod u8x32_;
379pub use u8x32_::*;
380
381mod u8x64_;
382pub use u8x64_::*;
383
384mod u16x8_;
385pub use u16x8_::*;
386
387mod u16x16_;
388pub use u16x16_::*;
389
390mod u16x32_;
391pub use u16x32_::*;
392
393mod u32x4_;
394pub use u32x4_::*;
395
396mod u32x8_;
397pub use u32x8_::*;
398
399mod u32x16_;
400pub use u32x16_::*;
401
402mod u64x2_;
403pub use u64x2_::*;
404
405mod u64x4_;
406pub use u64x4_::*;
407
408mod u64x8_;
409pub use u64x8_::*;
410
411/// A mask for the low `W` bits, the only part of a 64-bit `add_mul_lo` or
412/// `add_mul_hi` operand that is read.
413#[inline]
414const fn add_mul_operand_mask_u64<const W: u32>() -> u64 {
415 const { assert!(W >= 1 && W <= 64, "`add_mul` width must be in `1..=64`") };
416 u64::MAX >> (64 - W)
417}
418
419/// One `u64` lane of `add_mul_lo`: `acc + ((a * b) mod 2^W)`.
420#[inline]
421fn add_mul_lo_lane_u64<const W: u32>(acc: u64, a: u64, b: u64) -> u64 {
422 let mask = add_mul_operand_mask_u64::<W>();
423 let product = (a & mask) as u128 * (b & mask) as u128;
424 acc.wrapping_add(product as u64 & mask)
425}
426
427/// One `u64` lane of `add_mul_hi`: `acc + ((a * b) >> W)`.
428#[inline]
429fn add_mul_hi_lane_u64<const W: u32>(acc: u64, a: u64, b: u64) -> u64 {
430 let mask = add_mul_operand_mask_u64::<W>();
431 let product = (a & mask) as u128 * (b & mask) as u128;
432 acc.wrapping_add((product >> W) as u64)
433}
434
435/// A mask for the low `W` bits, the only part of a 32-bit `add_mul_lo` or
436/// `add_mul_hi` operand that is read.
437#[inline]
438const fn add_mul_operand_mask_u32<const W: u32>() -> u32 {
439 const { assert!(W >= 1 && W <= 32, "`add_mul` width must be in `1..=32`") };
440 u32::MAX >> (32 - W)
441}
442
443/// One `u32` lane of `add_mul_lo`: `acc + ((a * b) mod 2^W)`.
444#[inline]
445fn add_mul_lo_lane_u32<const W: u32>(acc: u32, a: u32, b: u32) -> u32 {
446 let mask = add_mul_operand_mask_u32::<W>();
447 let product = (a & mask) as u64 * (b & mask) as u64;
448 acc.wrapping_add(product as u32 & mask)
449}
450
451/// One `u32` lane of `add_mul_hi`: `acc + ((a * b) >> W)`.
452#[inline]
453fn add_mul_hi_lane_u32<const W: u32>(acc: u32, a: u32, b: u32) -> u32 {
454 let mask = add_mul_operand_mask_u32::<W>();
455 let product = (a & mask) as u64 * (b & mask) as u64;
456 acc.wrapping_add((product >> W) as u32)
457}
458
459#[allow(dead_code)]
460fn generic_bit_blend<T>(mask: T, y: T, n: T) -> T
461where
462 T: Copy + BitXor<Output = T> + BitAnd<Output = T>,
463{
464 n ^ ((n ^ y) & mask)
465}
466
467#[allow(unused)]
468fn software_sqrt(x: f64) -> f64 {
469 use core::num::Wrapping;
470 type wu32 = Wrapping<u32>;
471 const fn w(u: u32) -> wu32 {
472 Wrapping(u)
473 }
474 let mut z: f64;
475 let sign: wu32 = w(0x80000000);
476 let mut ix0: i32;
477 let mut s0: i32;
478 let mut q: i32;
479 let mut m: i32;
480 let mut t: i32;
481 let mut i: i32;
482 let mut r: wu32;
483 let mut t1: wu32;
484 let mut s1: wu32;
485 let mut ix1: wu32;
486 let mut q1: wu32;
487 // extract data
488
489 pick! {
490 if #[cfg(target_endian = "little")]
491 {
492 let [low, high]: [u32; 2] = cast(x);
493 ix0 = high as i32;
494 ix1 = w(low);
495 }
496 else
497 {
498 let [high, low]: [u32; 2] = cast(x);
499 ix0 = high as i32;
500 ix1 = w(low);
501 }
502 }
503
504 // inf and nan
505 {
506 if x.is_nan() {
507 return f64::NAN;
508 }
509 if ix0 & 0x7ff00000 == 0x7ff00000 {
510 return x * x + x;
511 }
512 }
513 // handle zero
514 {
515 if ix0 <= 0 {
516 if ((ix0 & (!sign).0 as i32) | (ix1.0 as i32)) == 0 {
517 return x;
518 } else if ix0 < 0 {
519 return (x - x) / (x - x);
520 }
521 }
522 }
523 // normalize
524 {
525 m = ix0 >> 20;
526 if m == 0 {
527 // subnormal
528 while ix0 == 0 {
529 m -= 21;
530 ix0 |= (ix1 >> 11).0 as i32;
531 ix1 <<= 21;
532 }
533 i = 0;
534 while ix0 & 0x00100000 == 0 {
535 ix0 <<= 1;
536 i += 1;
537 }
538 m -= i - 1;
539 ix0 |= (ix1.0 >> (31 - i)) as i32;
540 ix1 <<= i as usize;
541 }
542 // un-bias exponent
543 m -= 1023;
544 ix0 = (ix0 & 0x000fffff) | 0x00100000;
545 if (m & 1) != 0 {
546 // odd m, double the input to make it even
547 ix0 += ix0 + ((ix1 & sign) >> 31).0 as i32;
548 ix1 += ix1;
549 }
550 m >>= 1;
551 }
552 // generate sqrt bit by bit
553 {
554 ix0 += ix0 + ((ix1 & sign) >> 31).0 as i32;
555 ix1 += ix1;
556 // q and q1 store the sqrt(x);
557 q = 0;
558 q1 = w(0);
559 s0 = 0;
560 s1 = w(0);
561 // our bit that moves from right to left
562 r = w(0x00200000);
563 while r != w(0) {
564 t = s0 + (r.0 as i32);
565 if t <= ix0 {
566 s0 = t + (r.0 as i32);
567 ix0 -= t;
568 q += (r.0 as i32);
569 }
570 ix0 += ix0 + ((ix1 & sign) >> 31).0 as i32;
571 ix1 += ix1;
572 r >>= 1;
573 }
574 r = sign;
575 while r != w(0) {
576 t1 = s1 + r;
577 t = s0;
578 if (t < ix0) || ((t == ix0) && (t1 <= ix1)) {
579 s1 = t1 + r;
580 if t1 & sign == sign && (s1 & sign) == w(0) {
581 s0 += 1;
582 }
583 ix0 -= t;
584 if ix1 < t1 {
585 ix0 -= 1;
586 }
587 ix1 -= t1;
588 q1 += r;
589 }
590 ix0 += ix0 + ((ix1 & sign) >> 31).0 as i32;
591 ix1 += ix1;
592 r >>= 1;
593 }
594 }
595 // use floating add to find out rounding direction
596 {
597 if ix0 | (ix1.0 as i32) != 0 {
598 z = 1.0 - 1.0e-300;
599 if z >= 1.0 {
600 z = 1.0 + 1.0e-300;
601 if q1 == w(0xffffffff) {
602 q1 = w(0);
603 q += 1;
604 } else if z > 1.0 {
605 if q1 == w(0xfffffffe) {
606 q += 1;
607 }
608 q1 += w(2);
609 } else {
610 q1 += q1 & w(1);
611 }
612 }
613 }
614 }
615 // finish up
616 ix0 = (q >> 1) + 0x3fe00000;
617 ix1 = q1 >> 1;
618 if q & 1 == 1 {
619 ix1 |= sign;
620 }
621 ix0 += m << 20;
622
623 pick! {
624 if #[cfg(target_endian = "little")]
625 {
626 cast::<[u32; 2], f64>([ix1.0, ix0 as u32])
627 }
628 else
629 {
630 cast::<[u32; 2], f64>([ix0 as u32, ix1.0])
631 }
632 }
633}
634
635#[test]
636fn test_software_sqrt() {
637 assert!(software_sqrt(f64::NAN).is_nan());
638 assert_eq!(software_sqrt(f64::INFINITY), f64::INFINITY);
639 assert_eq!(software_sqrt(0.0), 0.0);
640 assert_eq!(software_sqrt(-0.0), -0.0);
641 assert!(software_sqrt(-1.0).is_nan());
642 assert!(software_sqrt(f64::NEG_INFINITY).is_nan());
643 assert_eq!(software_sqrt(4.0), 2.0);
644 assert_eq!(software_sqrt(9.0), 3.0);
645 assert_eq!(software_sqrt(16.0), 4.0);
646 assert_eq!(software_sqrt(25.0), 5.0);
647 assert_eq!(software_sqrt(5000.0 * 5000.0), 5000.0);
648}
649
650/// A trait for lanewise SIMD selection.
651///
652/// The method [`select`] is used to select lanes from two SIMD values based on
653/// the elements of a SIMD [mask].
654///
655/// The type of values to select from could be a SIMD vector (e.g.,
656/// [`f32x4`]), or it could be a composite type over SIMD vectors (e.g., a
657/// `Vec3<f32x4>` structure).
658///
659/// This trait is meant to be implemented for such downstream types. If you
660/// simply want to call [`select`], you do not need to import this trait as SIMD
661/// vectors have an inherent method for this.
662///
663/// # Example
664///
665/// ```
666/// # use wide::{u32x4, Select};
667/// #
668/// #[derive(Debug, PartialEq)]
669/// struct Point {
670/// x: u32x4,
671/// y: u32x4,
672/// }
673///
674/// impl Select<Point> for u32x4 {
675/// fn select(self, if_true: Point, if_false: Point) -> Point {
676/// Point {
677/// x: self.select(if_true.x, if_false.x),
678/// y: self.select(if_true.y, if_false.y),
679/// }
680/// }
681/// }
682///
683/// let mask = u32x4::new([0, !0, !0, 0]);
684/// let if_true = Point { x: u32x4::splat(2), y: u32x4::ZERO };
685/// let if_false = Point { x: u32x4::ZERO, y: u32x4::splat(5) };
686///
687/// assert_eq!(
688/// mask.select(if_true, if_false),
689/// Point {
690/// x: u32x4::new([0, 2, 2, 0]),
691/// y: u32x4::new([5, 0, 0, 5]),
692/// },
693/// );
694/// ```
695///
696/// [`select`]: f32x4::select
697/// [mask]: crate#masks
698pub trait Select<T> {
699 /// Lanewise SIMD selection.
700 ///
701 /// If `self` is a SIMD vector, it is treated as a [mask], where
702 /// each lane is considered true if all bits are one, or false if all bits are
703 /// zero.
704 ///
705 /// For each lane:
706 ///
707 /// - If `self` is true, return the corresponding lane of `if_true`
708 /// - If `self` is false, return the corresponding lane of `if_false`
709 ///
710 /// [mask]: crate#masks
711 #[must_use]
712 fn select(self, if_true: T, if_false: T) -> T;
713}
714
715/// An extension trait implemented for arrays of SIMD vectors, which provides
716/// shuffling from multiple input vectors.
717///
718/// # Example
719///
720/// ```
721/// use wide::{f32x4, ShuffleExt, u32x4};
722///
723/// let simd_a = f32x4::new([0.0, 1.0, 2.0, 3.0]);
724/// let simd_b = f32x4::new([100.0, 101.0, 102.0, 103.0]);
725///
726/// // Here, indices `0..4` map to `simd_a`, and indices `4..8` map to `simd_b`
727/// let from_two_vectors = [simd_a, simd_b].shuffle(u32x4::new([2, 3, 4, 5]));
728///
729/// assert_eq!(from_two_vectors, f32x4::new([2.0, 3.0, 100.0, 101.0]));
730/// ```
731#[expect(private_bounds)]
732pub trait ShuffleExt: Sealed {
733 /// The type representing indices.
734 ///
735 /// This is always a SIMD vector of the unsigned integer with the same size as
736 /// `T` and the same number of elements.
737 type Indices;
738
739 /// The type returned by shuffle functions.
740 ///
741 /// This is always the type of SIMD vector contained in the array.
742 type Output;
743
744 /// Returns a SIMD vector whose elements are selected from multiple input
745 /// vectors using the corresponding runtime `indices`.
746 ///
747 /// If `N` is the number of elements in each vector, indices in the range
748 /// `0..N` select values from `self[0]`, indices in the range `N..N * 2`
749 /// select values from `self[1]`, etc.
750 ///
751 /// If an index is out of bounds (greater than or equal to `N * INPUTS`), the
752 /// corresponding result element is unspecified.
753 ///
754 /// # Example
755 ///
756 /// ```
757 /// use wide::{f32x4, ShuffleExt, u32x4};
758 ///
759 /// let simd_a = f32x4::new([0.0, 1.0, 2.0, 3.0]);
760 /// let simd_b = f32x4::new([100.0, 101.0, 102.0, 103.0]);
761 ///
762 /// // Here, indices `0..4` map to `simd_a`, and indices `4..8` map to `simd_b`
763 /// let from_two_vectors = [simd_a, simd_b].shuffle(u32x4::new([2, 3, 4, 5]));
764 ///
765 /// assert_eq!(from_two_vectors, f32x4::new([2.0, 3.0, 100.0, 101.0]));
766 /// ```
767 ///
768 /// # Type-specific guarantees
769 ///
770 /// For all 8-bit types, it is guaranteed that for out of bounds indices,
771 /// either zero is returned or the index wraps around, non-deterministically
772 /// (unlike other types, which can return arbitrary values).
773 #[must_use]
774 fn shuffle(self, indices: Self::Indices) -> Self::Output;
775
776 /// Returns a SIMD vector whose elements are selected from multiple input
777 /// vectors using the corresponding runtime `indices`.
778 ///
779 /// If `N` is the number of elements in each vector, indices in the range
780 /// `0..N` select values from `self[0]`, indices in the range `N..N * 2`
781 /// select values from `self[1]`, etc.
782 ///
783 /// If an index is out of bounds (greater than or equal to `N * INPUTS`), the
784 /// corresponding result element is the number zero.
785 ///
786 /// # Example
787 ///
788 /// ```
789 /// use wide::{f32x4, ShuffleExt, u32x4};
790 ///
791 /// let simd_a = f32x4::new([0.0, 1.0, 2.0, 3.0]);
792 /// let simd_b = f32x4::new([100.0, 101.0, 102.0, 103.0]);
793 ///
794 /// // Here, indices `0..4` map to `simd_a`, and indices `4..8` map to `simd_b`
795 /// let from_two_vectors = [simd_a, simd_b].shuffle_zeroing(u32x4::new([2, 3, 100, 5]));
796 ///
797 /// assert_eq!(from_two_vectors, f32x4::new([2.0, 3.0, 0.0, 101.0]));
798 /// ```
799 #[must_use]
800 fn shuffle_zeroing(self, indices: Self::Indices) -> Self::Output;
801
802 /// Returns a SIMD vector whose elements are selected from multiple input
803 /// vectors using the corresponding runtime `indices`.
804 ///
805 /// If `N` is the number of elements in each vector, indices in the range
806 /// `0..N` select values from `self[0]`, indices in the range `N..N * 2`
807 /// select values from `self[1]`, etc.
808 ///
809 /// Indices are wrapped by `N * INPUTS`.
810 ///
811 /// # Example
812 ///
813 /// ```
814 /// use wide::{f32x4, ShuffleExt, u32x4};
815 ///
816 /// let simd_a = f32x4::new([0.0, 1.0, 2.0, 3.0]);
817 /// let simd_b = f32x4::new([100.0, 101.0, 102.0, 103.0]);
818 ///
819 /// // Here, indices `0..4` map to `simd_a`, and indices `4..8` map to `simd_b`
820 /// let from_two_vectors = [simd_a, simd_b].shuffle_wrapping(u32x4::new([2, 3, 9, 5]));
821 ///
822 /// assert_eq!(from_two_vectors, f32x4::new([2.0, 3.0, 1.0, 101.0]));
823 /// ```
824 #[must_use]
825 fn shuffle_wrapping(self, indices: Self::Indices) -> Self::Output;
826}
827
828/// A deprecated trait for the [`simd_eq`] function.
829///
830/// [`simd_eq`]: f32x4::simd_eq
831#[deprecated(
832 since = "1.5.0",
833 note = "use the inherent function `simd_eq` instead"
834)]
835pub trait CmpEq<Rhs = Self> {
836 /// The type returned by [`simd_eq`].
837 ///
838 /// [`simd_eq`]: Self::simd_eq
839 type Output;
840
841 /// Returns a [mask] that checks if each element of `self` is equal to the
842 /// corresponding element of `rhs`.
843 ///
844 /// This is a method of the deprecated [`CmpEq`] trait. Use the inherent
845 /// function instead.
846 ///
847 /// [mask]: crate#masks
848 fn simd_eq(self, rhs: Rhs) -> Self::Output;
849}
850
851/// A deprecated trait for the [`simd_gt`] function.
852///
853/// [`simd_gt`]: f32x4::simd_gt
854#[deprecated(
855 since = "1.5.0",
856 note = "use the inherent function `simd_gt` instead"
857)]
858pub trait CmpGt<Rhs = Self> {
859 /// The type returned by [`simd_gt`].
860 ///
861 /// [`simd_gt`]: Self::simd_gt
862 type Output;
863
864 /// Returns a [mask] that checks if each element of `self` is greater than the
865 /// corresponding element of `rhs`.
866 ///
867 /// This is a method of the deprecated [`CmpGt`] trait. Use the inherent
868 /// function instead.
869 ///
870 /// [mask]: crate#masks
871 fn simd_gt(self, rhs: Rhs) -> Self::Output;
872}
873
874/// A deprecated trait for the [`simd_ge`] function.
875///
876/// [`simd_ge`]: f32x4::simd_ge
877#[deprecated(
878 since = "1.5.0",
879 note = "use the inherent function `simd_ge` instead"
880)]
881pub trait CmpGe<Rhs = Self> {
882 /// The type returned by [`simd_ge`].
883 ///
884 /// [`simd_ge`]: Self::simd_ge
885 type Output;
886
887 /// Returns a [mask] that checks if each element of `self` is greater than or
888 /// equal to the corresponding element of `rhs`.
889 ///
890 /// This is a method of the deprecated [`CmpGe`] trait. Use the inherent
891 /// function instead.
892 ///
893 /// [mask]: crate#masks
894 fn simd_ge(self, rhs: Rhs) -> Self::Output;
895}
896
897/// A deprecated trait for the [`simd_ne`] function.
898///
899/// [`simd_ne`]: f32x4::simd_ne
900#[deprecated(
901 since = "1.5.0",
902 note = "use the inherent function `simd_ne` instead"
903)]
904pub trait CmpNe<Rhs = Self> {
905 /// The type returned by [`simd_ne`].
906 ///
907 /// [`simd_ne`]: Self::simd_ne
908 type Output;
909
910 /// Returns a [mask] that checks if each element of `self` is not equal to the
911 /// corresponding element of `rhs`.
912 ///
913 /// This is a method of the deprecated [`CmpNe`] trait. Use the inherent
914 /// function instead.
915 ///
916 /// [mask]: crate#masks
917 fn simd_ne(self, rhs: Rhs) -> Self::Output;
918}
919
920/// A deprecated trait for the [`simd_lt`] function.
921///
922/// [`simd_lt`]: f32x4::simd_lt
923#[deprecated(
924 since = "1.5.0",
925 note = "use the inherent function `simd_lt` instead"
926)]
927pub trait CmpLt<Rhs = Self> {
928 /// The type returned by [`simd_lt`].
929 ///
930 /// [`simd_lt`]: Self::simd_lt
931 type Output;
932
933 /// Returns a [mask] that checks if each element of `self` is less than the
934 /// corresponding element of `rhs`.
935 ///
936 /// This is a method of the deprecated [`CmpLt`] trait. Use the inherent
937 /// function instead.
938 ///
939 /// [mask]: crate#masks
940 fn simd_lt(self, rhs: Rhs) -> Self::Output;
941}
942
943/// A deprecated trait for the [`simd_le`] function.
944///
945/// [`simd_le`]: f32x4::simd_le
946#[deprecated(
947 since = "1.5.0",
948 note = "use the inherent function `simd_le` instead"
949)]
950pub trait CmpLe<Rhs = Self> {
951 /// The type returned by [`simd_le`].
952 ///
953 /// [`simd_le`]: Self::simd_le
954 type Output;
955
956 /// Returns a [mask] that checks if each element of `self` is less than or
957 /// equal to the corresponding element of `rhs`.
958 ///
959 /// This is a method of the deprecated [`CmpLe`] trait. Use the inherent
960 /// function instead.
961 ///
962 /// [mask]: crate#masks
963 fn simd_le(self, rhs: Rhs) -> Self::Output;
964}
965
966/// A trait for SIMD variants of [`align_to`] functions.
967///
968/// [`align_to`]: https://doc.rust-lang.org/std/primitive.slice.html#method.align_to
969pub trait AlignTo
970where
971 Self: Pod + Default + PartialEq + From<Self::Elem>,
972 Self::Elem: Pod + Default + PartialEq,
973{
974 /// The element type of this SIMD vector.
975 type Elem;
976
977 /// A SIMD variant of [`align_to`].
978 ///
979 /// [`align_to`]: https://doc.rust-lang.org/std/primitive.slice.html#method.align_to
980 #[inline]
981 fn simd_align_to(
982 slice: &[Self::Elem],
983 ) -> (&[Self::Elem], &[Self], &[Self::Elem]) {
984 pod_align_to(slice)
985 }
986
987 /// A SIMD variant of [`align_to_mut`].
988 ///
989 /// [`align_to_mut`]: https://doc.rust-lang.org/std/primitive.slice.html#method.align_to_mut
990 #[inline]
991 fn simd_align_to_mut(
992 slice: &mut [Self::Elem],
993 ) -> (&mut [Self::Elem], &mut [Self], &mut [Self::Elem]) {
994 pod_align_to_mut(slice)
995 }
996}
997
998trait Sealed {}