|
1 | 1 | use crate::simd::{
|
2 | 2 | cmp::SimdPartialOrd,
|
| 3 | + num::SimdUint, |
3 | 4 | ptr::{SimdConstPtr, SimdMutPtr},
|
4 | 5 | LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle,
|
5 | 6 | };
|
@@ -262,6 +263,7 @@ where
|
262 | 263 | /// # Panics
|
263 | 264 | ///
|
264 | 265 | /// Panics if the slice's length is less than the vector's `Simd::N`.
|
| 266 | + /// Use `load_or_default` for an alternative that does not panic. |
265 | 267 | ///
|
266 | 268 | /// # Example
|
267 | 269 | ///
|
@@ -315,6 +317,143 @@ where
|
315 | 317 | unsafe { self.store(slice.as_mut_ptr().cast()) }
|
316 | 318 | }
|
317 | 319 |
|
| 320 | + /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for |
| 321 | + /// the `slice`. Otherwise, the default value for the element type is returned. |
| 322 | + /// |
| 323 | + /// # Examples |
| 324 | + /// ``` |
| 325 | + /// # #![feature(portable_simd)] |
| 326 | + /// # #[cfg(feature = "as_crate")] use core_simd::simd; |
| 327 | + /// # #[cfg(not(feature = "as_crate"))] use core::simd; |
| 328 | + /// # use simd::Simd; |
| 329 | + /// let vec: Vec<i32> = vec![10, 11]; |
| 330 | + /// |
| 331 | + /// let result = Simd::<i32, 4>::load_or_default(&vec); |
| 332 | + /// assert_eq!(result, Simd::from_array([10, 11, 0, 0])); |
| 333 | + /// ``` |
| 334 | + #[must_use] |
| 335 | + #[inline] |
| 336 | + pub fn load_or_default(slice: &[T]) -> Self |
| 337 | + where |
| 338 | + T: Default, |
| 339 | + { |
| 340 | + Self::load_or(slice, Default::default()) |
| 341 | + } |
| 342 | + |
| 343 | + /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for |
| 344 | + /// the `slice`. Otherwise, the corresponding value from `or` is passed through. |
| 345 | + /// |
| 346 | + /// # Examples |
| 347 | + /// ``` |
| 348 | + /// # #![feature(portable_simd)] |
| 349 | + /// # #[cfg(feature = "as_crate")] use core_simd::simd; |
| 350 | + /// # #[cfg(not(feature = "as_crate"))] use core::simd; |
| 351 | + /// # use simd::Simd; |
| 352 | + /// let vec: Vec<i32> = vec![10, 11]; |
| 353 | + /// let or = Simd::from_array([-5, -4, -3, -2]); |
| 354 | + /// |
| 355 | + /// let result = Simd::load_or(&vec, or); |
| 356 | + /// assert_eq!(result, Simd::from_array([10, 11, -3, -2])); |
| 357 | + /// ``` |
| 358 | + #[must_use] |
| 359 | + #[inline] |
| 360 | + pub fn load_or(slice: &[T], or: Self) -> Self { |
| 361 | + Self::load_select(slice, Mask::splat(true), or) |
| 362 | + } |
| 363 | + |
| 364 | + /// Reads contiguous elements from `slice`. Each element is read from memory if its |
| 365 | + /// corresponding element in `enable` is `true`. |
| 366 | + /// |
| 367 | + /// When the element is disabled or out of bounds for the slice, that memory location |
| 368 | + /// is not accessed and the corresponding value from `or` is passed through. |
| 369 | + /// |
| 370 | + /// # Examples |
| 371 | + /// ``` |
| 372 | + /// # #![feature(portable_simd)] |
| 373 | + /// # #[cfg(feature = "as_crate")] use core_simd::simd; |
| 374 | + /// # #[cfg(not(feature = "as_crate"))] use core::simd; |
| 375 | + /// # use simd::{Simd, Mask}; |
| 376 | + /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; |
| 377 | + /// let enable = Mask::from_array([true, true, false, true]); |
| 378 | + /// let or = Simd::from_array([-5, -4, -3, -2]); |
| 379 | + /// |
| 380 | + /// let result = Simd::load_select(&vec, enable, or); |
| 381 | + /// assert_eq!(result, Simd::from_array([10, 11, -3, 13])); |
| 382 | + /// ``` |
| 383 | + #[must_use] |
| 384 | + #[inline] |
| 385 | + pub fn load_select_or_default(slice: &[T], enable: Mask<<T as SimdElement>::Mask, N>) -> Self |
| 386 | + where |
| 387 | + T: Default, |
| 388 | + { |
| 389 | + Self::load_select(slice, enable, Default::default()) |
| 390 | + } |
| 391 | + |
| 392 | + /// Reads contiguous elements from `slice`. Each element is read from memory if its |
| 393 | + /// corresponding element in `enable` is `true`. |
| 394 | + /// |
| 395 | + /// When the element is disabled or out of bounds for the slice, that memory location |
| 396 | + /// is not accessed and the corresponding value from `or` is passed through. |
| 397 | + /// |
| 398 | + /// # Examples |
| 399 | + /// ``` |
| 400 | + /// # #![feature(portable_simd)] |
| 401 | + /// # #[cfg(feature = "as_crate")] use core_simd::simd; |
| 402 | + /// # #[cfg(not(feature = "as_crate"))] use core::simd; |
| 403 | + /// # use simd::{Simd, Mask}; |
| 404 | + /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; |
| 405 | + /// let enable = Mask::from_array([true, true, false, true]); |
| 406 | + /// let or = Simd::from_array([-5, -4, -3, -2]); |
| 407 | + /// |
| 408 | + /// let result = Simd::load_select(&vec, enable, or); |
| 409 | + /// assert_eq!(result, Simd::from_array([10, 11, -3, 13])); |
| 410 | + /// ``` |
| 411 | + #[must_use] |
| 412 | + #[inline] |
| 413 | + pub fn load_select( |
| 414 | + slice: &[T], |
| 415 | + mut enable: Mask<<T as SimdElement>::Mask, N>, |
| 416 | + or: Self, |
| 417 | + ) -> Self { |
| 418 | + enable &= mask_up_to(slice.len()); |
| 419 | + // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to |
| 420 | + // the element. |
| 421 | + unsafe { Self::load_select_ptr(slice.as_ptr(), enable, or) } |
| 422 | + } |
| 423 | + |
| 424 | + /// Reads contiguous elements from `slice`. Each element is read from memory if its |
| 425 | + /// corresponding element in `enable` is `true`. |
| 426 | + /// |
| 427 | + /// When the element is disabled, that memory location is not accessed and the corresponding |
| 428 | + /// value from `or` is passed through. |
| 429 | + #[must_use] |
| 430 | + #[inline] |
| 431 | + pub unsafe fn load_select_unchecked( |
| 432 | + slice: &[T], |
| 433 | + enable: Mask<<T as SimdElement>::Mask, N>, |
| 434 | + or: Self, |
| 435 | + ) -> Self { |
| 436 | + let ptr = slice.as_ptr(); |
| 437 | + // SAFETY: The safety of reading elements from `slice` is ensured by the caller. |
| 438 | + unsafe { Self::load_select_ptr(ptr, enable, or) } |
| 439 | + } |
| 440 | + |
| 441 | + /// Reads contiguous elements starting at `ptr`. Each element is read from memory if its |
| 442 | + /// corresponding element in `enable` is `true`. |
| 443 | + /// |
| 444 | + /// When the element is disabled, that memory location is not accessed and the corresponding |
| 445 | + /// value from `or` is passed through. |
| 446 | + #[must_use] |
| 447 | + #[inline] |
| 448 | + pub unsafe fn load_select_ptr( |
| 449 | + ptr: *const T, |
| 450 | + enable: Mask<<T as SimdElement>::Mask, N>, |
| 451 | + or: Self, |
| 452 | + ) -> Self { |
| 453 | + // SAFETY: The safety of reading elements through `ptr` is ensured by the caller. |
| 454 | + unsafe { core::intrinsics::simd::simd_masked_load(enable.to_int(), ptr, or) } |
| 455 | + } |
| 456 | + |
318 | 457 | /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
|
319 | 458 | /// If an index is out-of-bounds, the element is instead selected from the `or` vector.
|
320 | 459 | ///
|
@@ -493,6 +632,77 @@ where
|
493 | 632 | unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) }
|
494 | 633 | }
|
495 | 634 |
|
| 635 | + /// Conditionally write contiguous elements to `slice`. The `enable` mask controls |
| 636 | + /// which elements are written, as long as they're in-bounds of the `slice`. |
| 637 | + /// If the element is disabled or out of bounds, no memory access to that location |
| 638 | + /// is made. |
| 639 | + /// |
| 640 | + /// # Examples |
| 641 | + /// ``` |
| 642 | + /// # #![feature(portable_simd)] |
| 643 | + /// # #[cfg(feature = "as_crate")] use core_simd::simd; |
| 644 | + /// # #[cfg(not(feature = "as_crate"))] use core::simd; |
| 645 | + /// # use simd::{Simd, Mask}; |
| 646 | + /// let mut arr = [0i32; 4]; |
| 647 | + /// let write = Simd::from_array([-5, -4, -3, -2]); |
| 648 | + /// let enable = Mask::from_array([false, true, true, true]); |
| 649 | + /// |
| 650 | + /// write.store_select(&mut arr[..3], enable); |
| 651 | + /// assert_eq!(arr, [0, -4, -3, 0]); |
| 652 | + /// ``` |
| 653 | + #[inline] |
| 654 | + pub fn store_select(self, slice: &mut [T], mut enable: Mask<<T as SimdElement>::Mask, N>) { |
| 655 | + enable &= mask_up_to(slice.len()); |
| 656 | + // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to |
| 657 | + // the element. |
| 658 | + unsafe { self.store_select_ptr(slice.as_mut_ptr(), enable) } |
| 659 | + } |
| 660 | + |
| 661 | + /// Conditionally write contiguous elements to `slice`. The `enable` mask controls |
| 662 | + /// which elements are written. |
| 663 | + /// |
| 664 | + /// # Safety |
| 665 | + /// |
| 666 | + /// Every enabled element must be in bounds for the `slice`. |
| 667 | + /// |
| 668 | + /// # Examples |
| 669 | + /// ``` |
| 670 | + /// # #![feature(portable_simd)] |
| 671 | + /// # #[cfg(feature = "as_crate")] use core_simd::simd; |
| 672 | + /// # #[cfg(not(feature = "as_crate"))] use core::simd; |
| 673 | + /// # use simd::{Simd, Mask}; |
| 674 | + /// let mut arr = [0i32; 4]; |
| 675 | + /// let write = Simd::from_array([-5, -4, -3, -2]); |
| 676 | + /// let enable = Mask::from_array([false, true, true, true]); |
| 677 | + /// |
| 678 | + /// unsafe { write.store_select_unchecked(&mut arr, enable) }; |
| 679 | + /// assert_eq!(arr, [0, -4, -3, -2]); |
| 680 | + /// ``` |
| 681 | + #[inline] |
| 682 | + pub unsafe fn store_select_unchecked( |
| 683 | + self, |
| 684 | + slice: &mut [T], |
| 685 | + enable: Mask<<T as SimdElement>::Mask, N>, |
| 686 | + ) { |
| 687 | + let ptr = slice.as_mut_ptr(); |
| 688 | + // SAFETY: The safety of writing elements in `slice` is ensured by the caller. |
| 689 | + unsafe { self.store_select_ptr(ptr, enable) } |
| 690 | + } |
| 691 | + |
| 692 | + /// Conditionally write contiguous elements starting from `ptr`. |
| 693 | + /// The `enable` mask controls which elements are written. |
| 694 | + /// When disabled, the memory location corresponding to that element is not accessed. |
| 695 | + /// |
| 696 | + /// # Safety |
| 697 | + /// |
| 698 | + /// Memory addresses for element are calculated [`pointer::wrapping_offset`] and |
| 699 | + /// each enabled element must satisfy the same conditions as [`core::ptr::write`]. |
| 700 | + #[inline] |
| 701 | + pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<<T as SimdElement>::Mask, N>) { |
| 702 | + // SAFETY: The safety of writing elements through `ptr` is ensured by the caller. |
| 703 | + unsafe { core::intrinsics::simd::simd_masked_store(enable.to_int(), ptr, self) } |
| 704 | + } |
| 705 | + |
496 | 706 | /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`.
|
497 | 707 | /// If an index is out-of-bounds, the write is suppressed without panicking.
|
498 | 708 | /// If two elements in the scattered vector would write to the same index
|
@@ -980,3 +1190,37 @@ where
|
980 | 1190 | {
|
981 | 1191 | type Mask = isize;
|
982 | 1192 | }
|
| 1193 | + |
| 1194 | +#[inline] |
| 1195 | +fn lane_indices<const N: usize>() -> Simd<usize, N> |
| 1196 | +where |
| 1197 | + LaneCount<N>: SupportedLaneCount, |
| 1198 | +{ |
| 1199 | + let mut index = [0; N]; |
| 1200 | + for i in 0..N { |
| 1201 | + index[i] = i; |
| 1202 | + } |
| 1203 | + Simd::from_array(index) |
| 1204 | +} |
| 1205 | + |
| 1206 | +#[inline] |
| 1207 | +fn mask_up_to<M, const N: usize>(len: usize) -> Mask<M, N> |
| 1208 | +where |
| 1209 | + LaneCount<N>: SupportedLaneCount, |
| 1210 | + M: MaskElement, |
| 1211 | +{ |
| 1212 | + let index = lane_indices::<N>(); |
| 1213 | + let max_value: u64 = M::max_unsigned(); |
| 1214 | + macro_rules! case { |
| 1215 | + ($ty:ty) => { |
| 1216 | + if N < <$ty>::MAX as usize && max_value as $ty as u64 == max_value { |
| 1217 | + return index.cast().simd_lt(Simd::splat(len.min(N) as $ty)).cast(); |
| 1218 | + } |
| 1219 | + }; |
| 1220 | + } |
| 1221 | + case!(u8); |
| 1222 | + case!(u16); |
| 1223 | + case!(u32); |
| 1224 | + case!(u64); |
| 1225 | + index.simd_lt(Simd::splat(len)).cast() |
| 1226 | +} |
0 commit comments