1use crate::align::Alignment;
4use crate::arch::SimdArch;
5use crate::execution::{ExecutionMode, Unmasked};
6use crate::iter;
7use crate::kernel::SimdKernel;
8use crate::scalar::Scalar;
9use core::marker::PhantomData;
10
11pub mod gather;
13pub mod masked;
15pub mod ops;
17pub mod ops_mut;
19pub mod reduce;
21pub mod scan;
23pub mod select;
25pub mod tile;
27pub mod unary;
29
30pub use tile::{TileMatrixMultiply, TileView};
31
32pub mod mask_reg;
34pub mod vector_ops;
36pub mod vector_reg;
38
39pub use mask_reg::Mask;
40pub use vector_reg::Vector;
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum SimdError {
45 LengthMismatch,
47 InsufficientInputLength,
49 InsufficientOutputLength,
51 UnalignedAddress,
53 IndexOutOfBounds,
55 UnsupportedTarget,
57}
58
59impl core::fmt::Display for SimdError {
60 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
61 match self {
62 Self::LengthMismatch => write!(f, "Operand views have mismatched lengths"),
63 Self::InsufficientInputLength => write!(f, "Input slice has insufficient length"),
64 Self::InsufficientOutputLength => write!(f, "Output slice has insufficient length"),
65 Self::UnalignedAddress => {
66 write!(f, "Memory address does not satisfy alignment constraints")
67 }
68 Self::IndexOutOfBounds => write!(f, "Index is out of bounds of the view"),
69 Self::UnsupportedTarget => {
70 write!(f, "SIMD target is not supported or enabled on this host")
71 }
72 }
73 }
74}
75
76#[cfg(feature = "std")]
78impl std::error::Error for SimdError {}
79
80#[repr(transparent)]
91pub struct SimdView<
92 'a,
93 T: 'a,
94 Arch: SimdArch,
95 Align: Alignment,
96 Mode: ExecutionMode = Unmasked,
97 Ref: 'a = &'a [T],
98> {
99 ptr: *mut [T],
100 _marker: PhantomData<(&'a T, Arch, Align, Mode, Ref)>,
101}
102
103unsafe impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode, Ref: 'a> Send
104 for SimdView<'a, T, Arch, Align, Mode, Ref>
105where
106 Ref: Send,
107{
108}
109
110unsafe impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode, Ref: 'a> Sync
111 for SimdView<'a, T, Arch, Align, Mode, Ref>
112where
113 Ref: Sync,
114{
115}
116
117impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode> Clone
118 for SimdView<'a, T, Arch, Align, Mode, &'a [T]>
119{
120 #[inline(always)]
121 fn clone(&self) -> Self {
122 *self
123 }
124}
125
126impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode> Copy
127 for SimdView<'a, T, Arch, Align, Mode, &'a [T]>
128{
129}
130
131impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
132 SimdView<'a, T, Arch, Align, Mode, &'a [T]>
133{
134 #[inline]
143 pub fn new(data: &'a [T]) -> Option<Self> {
144 if !Arch::is_runtime_supported() {
145 return None;
146 }
147 if Align::IS_ALIGNED {
148 let req_align = Arch::REGISTER_WIDTH_BITS as usize / 8;
149 if req_align > 0 && Align::ALIGN_BYTES < req_align {
150 return None;
151 }
152 let addr = data.as_ptr() as usize;
153 if addr % Align::ALIGN_BYTES != 0 {
154 return None;
155 }
156 }
157 Some(Self {
158 ptr: data as *const [T] as *mut [T],
159 _marker: PhantomData,
160 })
161 }
162}
163
164impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
165 SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
166{
167 #[inline]
172 pub fn new_mut(data: &'a mut [T]) -> Option<Self> {
173 if !Arch::is_runtime_supported() {
174 return None;
175 }
176 if Align::IS_ALIGNED {
177 let req_align = Arch::REGISTER_WIDTH_BITS as usize / 8;
178 if req_align > 0 && Align::ALIGN_BYTES < req_align {
179 return None;
180 }
181 let addr = data.as_ptr() as usize;
182 if addr % Align::ALIGN_BYTES != 0 {
183 return None;
184 }
185 }
186 Some(Self {
187 ptr: data as *mut [T],
188 _marker: PhantomData,
189 })
190 }
191
192 #[inline(always)]
194 pub fn as_slice_mut(&mut self) -> &mut [T] {
195 unsafe { &mut *self.ptr }
196 }
197
198 #[inline(always)]
200 pub fn downgrade(self) -> SimdView<'a, T, Arch, Align, Mode, &'a [T]> {
201 SimdView {
202 ptr: self.ptr,
203 _marker: PhantomData,
204 }
205 }
206}
207
208impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode, Ref: 'a>
209 SimdView<'a, T, Arch, Align, Mode, Ref>
210{
211 #[inline(always)]
213 pub fn as_slice(&self) -> &[T] {
214 unsafe { &*self.ptr }
215 }
216
217 #[inline(always)]
219 pub fn len(&self) -> usize {
220 self.as_slice().len()
221 }
222
223 #[inline(always)]
225 pub fn is_empty(&self) -> bool {
226 self.len() == 0
227 }
228
229 #[inline(always)]
231 pub fn into_unaligned(self) -> SimdView<'a, T, Arch, crate::align::Unaligned, Mode, Ref> {
232 SimdView {
233 ptr: self.ptr,
234 _marker: PhantomData,
235 }
236 }
237
238 #[inline]
241 pub fn try_into_aligned<const A: usize>(
242 self,
243 ) -> Option<SimdView<'a, T, Arch, crate::align::Aligned<A>, Mode, Ref>> {
244 let req_align = Arch::REGISTER_WIDTH_BITS as usize / 8;
245 if req_align > 0 && A < req_align {
246 return None;
247 }
248 let addr = self.as_slice().as_ptr() as usize;
249 if addr % A == 0 {
250 Some(SimdView {
251 ptr: self.ptr,
252 _marker: PhantomData,
253 })
254 } else {
255 None
256 }
257 }
258}
259
260impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
261 SimdView<'a, T, Arch, Align, Mode, &'a [T]>
262{
263 #[inline]
265 pub fn slice_unaligned(
266 self,
267 range: core::ops::Range<usize>,
268 ) -> SimdView<'a, T, Arch, crate::align::Unaligned, Mode, &'a [T]> {
269 let sub = &self.as_slice()[range];
270 SimdView {
271 ptr: sub as *const [T] as *mut [T],
272 _marker: PhantomData,
273 }
274 }
275
276 #[inline]
279 pub fn slice_aligned<const A: usize>(
280 self,
281 range: core::ops::Range<usize>,
282 ) -> Option<SimdView<'a, T, Arch, crate::align::Aligned<A>, Mode, &'a [T]>> {
283 let req_align = Arch::REGISTER_WIDTH_BITS as usize / 8;
284 if req_align > 0 && A < req_align {
285 return None;
286 }
287 let sub = &self.as_slice()[range];
288 let addr = sub.as_ptr() as usize;
289 if addr % A == 0 {
290 Some(SimdView {
291 ptr: sub as *const [T] as *mut [T],
292 _marker: PhantomData,
293 })
294 } else {
295 None
296 }
297 }
298}
299
300impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
301 SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
302{
303 #[inline]
305 pub fn slice_unaligned_mut(
306 mut self,
307 range: core::ops::Range<usize>,
308 ) -> SimdView<'a, T, Arch, crate::align::Unaligned, Mode, &'a mut [T]> {
309 let sub = &mut self.as_slice_mut()[range];
310 SimdView {
311 ptr: sub as *mut [T],
312 _marker: PhantomData,
313 }
314 }
315
316 #[inline]
319 pub fn slice_aligned_mut<const A: usize>(
320 mut self,
321 range: core::ops::Range<usize>,
322 ) -> Option<SimdView<'a, T, Arch, crate::align::Aligned<A>, Mode, &'a mut [T]>> {
323 let req_align = Arch::REGISTER_WIDTH_BITS as usize / 8;
324 if req_align > 0 && A < req_align {
325 return None;
326 }
327 let sub = &mut self.as_slice_mut()[range];
328 let addr = sub.as_ptr() as usize;
329 if addr % A == 0 {
330 Some(SimdView {
331 ptr: sub as *mut [T],
332 _marker: PhantomData,
333 })
334 } else {
335 None
336 }
337 }
338}
339
340impl<
341 'a,
342 T: Scalar + 'a,
343 Arch: SimdArch + SimdKernel<T>,
344 Align: Alignment,
345 Mode: ExecutionMode,
346 Ref: 'a,
347 > SimdView<'a, T, Arch, Align, Mode, Ref>
348{
349 #[inline(always)]
355 pub fn simd_chunks(&self) -> iter::SimdChunks<'a, T, Arch, Align, Mode> {
356 unsafe {
358 iter::SimdChunks::from_raw_parts(self.as_slice().as_ptr(), self.len(), Arch::LANE_COUNT)
359 }
360 }
361
362 #[inline(always)]
368 pub fn zip_chunks<'b>(
369 &self,
370 other: &'b SimdView<'b, T, Arch, Align, Mode, &'b [T]>,
371 ) -> iter::ZipChunks<'a, 'b, T, Arch, Align, Mode> {
372 unsafe {
374 iter::ZipChunks::from_raw_parts(
375 self.as_slice().as_ptr(),
376 self.len(),
377 other.as_slice().as_ptr(),
378 other.len(),
379 )
380 }
381 }
382}
383
384impl<'a, T: Scalar + 'a, Arch: SimdArch + SimdKernel<T>, Align: Alignment, Mode: ExecutionMode>
385 SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
386{
387 #[inline(always)]
393 pub fn simd_chunks_mut(self) -> iter::SimdChunksMut<'a, T, Arch, Align, Mode> {
394 unsafe {
396 iter::SimdChunksMut::from_raw_parts(self.ptr as *mut T, self.len(), Arch::LANE_COUNT)
397 }
398 }
399
400 #[inline(always)]
414 pub fn zip_chunks_mut<'b>(
415 self,
416 other: &'b SimdView<'b, T, Arch, Align, Mode, &'b [T]>,
417 ) -> iter::ZipChunksMut<'a, 'b, T, Arch, Align, Mode> {
418 unsafe {
421 iter::ZipChunksMut::from_raw_parts(
422 self.ptr as *mut T,
423 self.len(),
424 other.as_slice().as_ptr(),
425 other.len(),
426 )
427 }
428 }
429}
430
431impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode, Ref: 'a> core::ops::Deref
432 for SimdView<'a, T, Arch, Align, Mode, Ref>
433{
434 type Target = [T];
435
436 #[inline(always)]
437 fn deref(&self) -> &Self::Target {
438 self.as_slice()
439 }
440}
441
442impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode> core::ops::DerefMut
443 for SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
444{
445 #[inline(always)]
446 fn deref_mut(&mut self) -> &mut Self::Target {
447 self.as_slice_mut()
448 }
449}
450
451impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
452 SimdView<'a, T, Arch, Align, Mode, &'a [T]>
453where
454 T: bytemuck::Pod,
455{
456 #[inline]
458 pub fn cast<U: bytemuck::Pod>(self) -> Option<SimdView<'a, U, Arch, Align, Mode, &'a [U]>> {
459 let casted = bytemuck::try_cast_slice(unsafe { &*self.ptr }).ok()?;
460 SimdView::new(casted)
461 }
462}
463
464impl<'a, T: 'a, Arch: SimdArch, Align: Alignment, Mode: ExecutionMode>
465 SimdView<'a, T, Arch, Align, Mode, &'a mut [T]>
466where
467 T: bytemuck::Pod,
468{
469 #[inline]
471 pub fn cast_mut<U: bytemuck::Pod>(
472 self,
473 ) -> Option<SimdView<'a, U, Arch, Align, Mode, &'a mut [U]>> {
474 let casted = bytemuck::try_cast_slice_mut(unsafe { &mut *self.ptr }).ok()?;
475 SimdView::new_mut(casted)
476 }
477}
478
479#[inline(never)]
480pub(crate) fn check_lengths_equal(len1: usize, len2: usize) -> Result<(), SimdError> {
481 if len1 != len2 {
482 return Err(SimdError::LengthMismatch);
483 }
484 Ok(())
485}
486
487#[inline(never)]
488pub(crate) fn check_output_length(input_len: usize, output_len: usize) -> Result<(), SimdError> {
489 if output_len < input_len {
490 return Err(SimdError::InsufficientOutputLength);
491 }
492 Ok(())
493}