1use core::mem::MaybeUninit;
9
10use crate::{CopyPlan, Exec, RawStridedMut, RawStridedRef, Result, StridedError};
11
12type AxisVec<T> = crate::inline::InlineVec<T>;
13
14#[derive(Clone, Debug)]
19pub struct SlicePlan {
20 operand_dims: AxisVec<usize>,
21 operand_strides: AxisVec<isize>,
22 dest_dims: AxisVec<usize>,
23 dest_strides: AxisVec<isize>,
24 source_strides: AxisVec<isize>,
25 source_offset_delta: isize,
26 copy_plan: CopyPlan,
27}
28
29#[derive(Clone, Debug)]
33pub struct ReversePlan {
34 operand_dims: AxisVec<usize>,
35 operand_strides: AxisVec<isize>,
36 dest_strides: AxisVec<isize>,
37 source_strides: AxisVec<isize>,
38 source_offset_delta: isize,
39 copy_plan: CopyPlan,
40}
41
42#[derive(Clone, Debug)]
50pub struct PadPlan {
51 operand_dims: AxisVec<usize>,
52 operand_strides: AxisVec<isize>,
53 dest_dims: AxisVec<usize>,
54 dest_strides: AxisVec<isize>,
55 edge_padding_low: AxisVec<i64>,
56 interior_step: AxisVec<i64>,
57 operand_total: usize,
58 dest_total: usize,
59 contiguous_dest_fill: bool,
60 contiguous_axis0_run: Option<ContiguousPadAxis0Run>,
61 contiguous_axis0_columns: Option<ContiguousPadAxis0Columns>,
62 generic_fill: PadFillCursor,
63 generic_copy: PadCopyCursor,
64}
65
66#[derive(Clone, Debug)]
67struct PadFillCursor {
68 steps: AxisVec<isize>,
69 resets: AxisVec<isize>,
70}
71
72#[derive(Clone, Debug)]
73struct PadCopyCursor {
74 shape: AxisVec<usize>,
75 source_base_delta: isize,
76 dest_base_delta: isize,
77 source_steps: AxisVec<isize>,
78 source_resets: AxisVec<isize>,
79 dest_steps: AxisVec<isize>,
80 dest_resets: AxisVec<isize>,
81 total: usize,
82}
83
84#[derive(Clone, Copy, Debug, Eq, PartialEq)]
85struct ContiguousPadAxis0Run {
86 operand_start: usize,
87 dest_start: usize,
88 len: usize,
89}
90
91#[derive(Clone, Debug)]
101struct ContiguousPadAxis0Columns {
102 column_total: usize,
104 dest_axis0_len: usize,
105 dest_axis0_step: isize,
106 low0: usize,
107 high0: usize,
108 run_len: usize,
109 operand_outer_dims: AxisVec<usize>,
110 operand_outer_strides: AxisVec<isize>,
111 dest_outer_dims: AxisVec<usize>,
112 edge_low_outer: AxisVec<usize>,
113}
114
115#[derive(Clone, Debug)]
126pub struct ConcatenatePlan {
127 input_dims: crate::inline::OperandVec<AxisVec<usize>>,
128 input_strides: crate::inline::OperandVec<AxisVec<isize>>,
129 dest_dims: AxisVec<usize>,
130 dest_strides: AxisVec<isize>,
131 dest_offset_deltas: crate::inline::OperandVec<isize>,
132 segment_starts: crate::inline::OperandVec<usize>,
135 copy_plans: crate::inline::OperandVec<CopyPlan>,
136}
137
138impl SlicePlan {
139 #[allow(clippy::too_many_arguments)]
141 pub fn compile(
142 operand_dims: &[usize],
143 operand_strides: &[isize],
144 dest_dims: &[usize],
145 dest_strides: &[isize],
146 starts: &[usize],
147 limits: &[usize],
148 slice_strides: &[usize],
149 ) -> Result<Self> {
150 let rank = operand_dims.len();
151 if operand_strides.len() != rank || dest_dims.len() != rank || dest_strides.len() != rank {
152 return Err(StridedError::StrideLengthMismatch);
153 }
154 if starts.len() != rank {
155 return Err(StridedError::RankMismatch(starts.len(), rank));
156 }
157 if limits.len() != rank {
158 return Err(StridedError::RankMismatch(limits.len(), rank));
159 }
160 if slice_strides.len() != rank {
161 return Err(StridedError::RankMismatch(slice_strides.len(), rank));
162 }
163 checked_total_len(operand_dims)?;
164 checked_total_len(dest_dims)?;
165
166 let mut expected_dest_dims: AxisVec<usize> = AxisVec::with_capacity(rank);
167 let mut source_strides: AxisVec<isize> = AxisVec::with_capacity(rank);
168 let mut source_offset_delta = 0isize;
169 for axis in 0..rank {
170 let start = starts[axis];
171 let limit = limits[axis];
172 let stride = slice_strides[axis];
173 if start > limit || limit > operand_dims[axis] || stride == 0 {
174 return Err(StridedError::InvalidAxis { axis, rank });
175 }
176 let span = limit - start;
177 expected_dest_dims.push(span.div_ceil(stride));
178 source_strides.push(checked_stride_mul(operand_strides[axis], stride)?);
179 source_offset_delta =
180 checked_offset_add(source_offset_delta, operand_strides[axis], start)?;
181 }
182 if dest_dims != &expected_dest_dims[..] {
183 return Err(StridedError::ShapeMismatch(
184 dest_dims.to_vec(),
185 expected_dest_dims.to_vec(),
186 ));
187 }
188 let copy_plan = CopyPlan::compile(dest_dims, dest_strides, &source_strides)?;
189
190 Ok(Self {
191 operand_dims: operand_dims.into(),
192 operand_strides: operand_strides.into(),
193 dest_dims: dest_dims.into(),
194 dest_strides: dest_strides.into(),
195 source_strides,
196 source_offset_delta,
197 copy_plan,
198 })
199 }
200
201 pub fn execute<T>(
203 &self,
204 dest: &mut RawStridedMut<'_, T>,
205 operand: &RawStridedRef<'_, T>,
206 exec: Exec<'_>,
207 ) -> Result<()>
208 where
209 T: Copy + Send + Sync,
210 {
211 self.check_call(dest, operand)?;
212 let source_offset = operand
213 .offset()
214 .checked_add(self.source_offset_delta)
215 .ok_or(StridedError::OffsetOverflow)?;
216 let source = unsafe {
217 RawStridedRef::new_unchecked(
218 operand.data(),
219 &self.dest_dims,
220 &self.source_strides,
221 source_offset,
222 )
223 };
224 self.copy_plan.execute(dest, &source, exec)
225 }
226
227 pub fn execute_uninit<T>(
229 &self,
230 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
231 operand: &RawStridedRef<'_, T>,
232 exec: Exec<'_>,
233 ) -> Result<()>
234 where
235 T: Copy + Send + Sync,
236 {
237 self.check_call(dest, operand)?;
238 let source_offset = operand
239 .offset()
240 .checked_add(self.source_offset_delta)
241 .ok_or(StridedError::OffsetOverflow)?;
242 let source = unsafe {
243 RawStridedRef::new_unchecked(
244 operand.data(),
245 &self.dest_dims,
246 &self.source_strides,
247 source_offset,
248 )
249 };
250 self.copy_plan.execute_uninit(dest, &source, exec)
251 }
252
253 fn check_call<D, T>(
254 &self,
255 dest: &RawStridedMut<'_, D>,
256 operand: &RawStridedRef<'_, T>,
257 ) -> Result<()> {
258 if operand.dims() != &self.operand_dims[..]
259 || operand.strides() != &self.operand_strides[..]
260 || dest.dims() != &self.dest_dims[..]
261 || dest.strides() != &self.dest_strides[..]
262 {
263 return Err(StridedError::PlanLayoutMismatch);
264 }
265 Ok(())
266 }
267}
268
269impl PadPlan {
270 #[allow(clippy::too_many_arguments)]
272 pub fn compile(
273 operand_dims: &[usize],
274 operand_strides: &[isize],
275 dest_dims: &[usize],
276 dest_strides: &[isize],
277 edge_padding_low: &[i64],
278 edge_padding_high: &[i64],
279 interior_padding: &[i64],
280 ) -> Result<Self> {
281 let rank = operand_dims.len();
282 if operand_strides.len() != rank || dest_dims.len() != rank || dest_strides.len() != rank {
283 return Err(StridedError::StrideLengthMismatch);
284 }
285 if edge_padding_low.len() != rank {
286 return Err(StridedError::RankMismatch(edge_padding_low.len(), rank));
287 }
288 if edge_padding_high.len() != rank {
289 return Err(StridedError::RankMismatch(edge_padding_high.len(), rank));
290 }
291 if interior_padding.len() != rank {
292 return Err(StridedError::RankMismatch(interior_padding.len(), rank));
293 }
294
295 let operand_total = checked_total_len(operand_dims)?;
296 let dest_total = checked_total_len(dest_dims)?;
297 if !crate::layout_check::is_injective_layout(dest_dims, dest_strides) {
298 return Err(StridedError::NonInjectiveOutputLayout);
299 }
300
301 let mut expected_dest_dims: AxisVec<usize> = AxisVec::with_capacity(rank);
302 let mut interior_step: AxisVec<i64> = AxisVec::with_capacity(rank);
303 for axis in 0..rank {
304 if interior_padding[axis] < 0 {
305 return Err(StridedError::InvalidAxis { axis, rank });
306 }
307 let step = interior_padding[axis]
308 .checked_add(1)
309 .ok_or(StridedError::OffsetOverflow)?;
310 interior_step.push(step);
311 expected_dest_dims.push(checked_pad_output_dim(
312 operand_dims[axis],
313 edge_padding_low[axis],
314 edge_padding_high[axis],
315 step,
316 axis,
317 rank,
318 )?);
319 }
320 if dest_dims != &expected_dest_dims[..] {
321 return Err(StridedError::ShapeMismatch(
322 dest_dims.to_vec(),
323 expected_dest_dims.to_vec(),
324 ));
325 }
326 let contiguous_dest_fill = is_dense_col_major(dest_dims, dest_strides);
327 let contiguous_axis0_run = compile_contiguous_pad_axis0_run(
328 operand_dims,
329 operand_strides,
330 dest_dims,
331 dest_strides,
332 edge_padding_low,
333 &interior_step,
334 );
335 let contiguous_axis0_columns = contiguous_axis0_run.and_then(|run| {
336 compile_contiguous_pad_axis0_columns(
337 operand_dims,
338 operand_strides,
339 dest_dims,
340 edge_padding_low,
341 edge_padding_high,
342 &interior_step,
343 contiguous_dest_fill,
344 run,
345 )
346 });
347 let generic_fill = compile_pad_fill_cursor(dest_dims, dest_strides)?;
348 let generic_copy = compile_pad_copy_cursor(
349 operand_dims,
350 operand_strides,
351 dest_dims,
352 dest_strides,
353 edge_padding_low,
354 &interior_step,
355 )?;
356
357 Ok(Self {
358 operand_dims: operand_dims.into(),
359 operand_strides: operand_strides.into(),
360 dest_dims: dest_dims.into(),
361 dest_strides: dest_strides.into(),
362 edge_padding_low: edge_padding_low.into(),
363 interior_step,
364 operand_total,
365 dest_total,
366 contiguous_dest_fill,
367 contiguous_axis0_run,
368 contiguous_axis0_columns,
369 generic_fill,
370 generic_copy,
371 })
372 }
373
374 pub fn execute<T>(
376 &self,
377 dest: &mut RawStridedMut<'_, T>,
378 operand: &RawStridedRef<'_, T>,
379 fill: T,
380 exec: Exec<'_>,
381 ) -> Result<()>
382 where
383 T: Copy + Send + Sync,
384 {
385 let mut exec = exec;
386 self.check_call(dest, operand)?;
387 if self.operand_total == 0 {
388 return self.fill_dest(dest, fill, exec);
389 }
390 if let Some(columns) = &self.contiguous_axis0_columns {
391 return self.execute_axis0_columns(dest, operand, fill, columns, exec);
392 }
393 self.fill_dest(dest, fill, exec.reborrow())?;
394 if let Some(run) = self.contiguous_axis0_run {
395 return self.copy_operand_axis0_runs(dest, operand, run, exec);
396 }
397 self.copy_operand(dest, operand, exec)
398 }
399
400 pub fn execute_uninit<T>(
402 &self,
403 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
404 operand: &RawStridedRef<'_, T>,
405 fill: T,
406 exec: Exec<'_>,
407 ) -> Result<()>
408 where
409 T: Copy + Send + Sync,
410 {
411 let mut exec = exec;
412 self.check_call(dest, operand)?;
413 if self.operand_total == 0 {
414 return self.fill_dest(dest, MaybeUninit::new(fill), exec);
415 }
416 if let Some(columns) = &self.contiguous_axis0_columns {
417 return self.execute_axis0_columns_uninit(dest, operand, fill, columns, exec);
418 }
419 self.fill_dest(dest, MaybeUninit::new(fill), exec.reborrow())?;
420 if let Some(run) = self.contiguous_axis0_run {
421 return self.copy_operand_axis0_runs_uninit(dest, operand, run, exec);
422 }
423 self.copy_operand_uninit(dest, operand, exec)
424 }
425
426 fn fill_dest<T>(&self, dest: &mut RawStridedMut<'_, T>, fill: T, exec: Exec<'_>) -> Result<()>
427 where
428 T: Copy + Send + Sync,
429 {
430 if self.dest_total == 0 {
431 return Ok(());
432 }
433 if self.contiguous_dest_fill {
434 let dest_offset =
435 usize::try_from(dest.offset()).map_err(|_| StridedError::OffsetOverflow)?;
436 let dest_end = dest_offset
437 .checked_add(self.dest_total)
438 .ok_or(StridedError::OffsetOverflow)?;
439 let dest_data = dest.data_mut();
440 let dest_slice = dest_data
441 .get_mut(dest_offset..dest_end)
442 .ok_or(StridedError::OffsetOverflow)?;
443 crate::threading::fill_contiguous(dest_slice, fill, exec);
444 return Ok(());
445 }
446 let exec = crate::threading::for_len(exec, self.dest_total);
447 if !exec.is_serial() {
448 return self.fill_dest_parallel(dest, fill, exec);
449 }
450 self.fill_dest_serial(dest, fill)
451 }
452
453 fn copy_operand_axis0_runs<T>(
454 &self,
455 dest: &mut RawStridedMut<'_, T>,
456 operand: &RawStridedRef<'_, T>,
457 run: ContiguousPadAxis0Run,
458 exec: Exec<'_>,
459 ) -> Result<()>
460 where
461 T: Copy + Send + Sync,
462 {
463 let dest_offset = dest.offset();
464 let dest_ptr = dest.data_mut().as_mut_ptr();
465 unsafe { self.copy_axis0_runs_raw(dest_ptr, dest_offset, operand, run, exec) }
469 }
470
471 fn copy_operand_axis0_runs_uninit<T>(
472 &self,
473 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
474 operand: &RawStridedRef<'_, T>,
475 run: ContiguousPadAxis0Run,
476 exec: Exec<'_>,
477 ) -> Result<()>
478 where
479 T: Copy + Send + Sync,
480 {
481 let dest_offset = dest.offset();
482 let dest_ptr = dest.data_mut().as_mut_ptr().cast::<T>();
483 unsafe { self.copy_axis0_runs_raw(dest_ptr, dest_offset, operand, run, exec) }
486 }
487
488 fn execute_axis0_columns<T>(
489 &self,
490 dest: &mut RawStridedMut<'_, T>,
491 operand: &RawStridedRef<'_, T>,
492 fill: T,
493 columns: &ContiguousPadAxis0Columns,
494 exec: Exec<'_>,
495 ) -> Result<()>
496 where
497 T: Copy + Send + Sync,
498 {
499 let dest_offset = dest.offset();
500 let dest_ptr = dest.data_mut().as_mut_ptr();
501 let operand_offset = operand.offset();
502 let operand_ptr = operand.data().as_ptr();
503 unsafe {
507 self.write_axis0_columns_raw::<T, false>(
508 dest_ptr,
509 dest_offset,
510 operand_ptr,
511 operand_offset,
512 fill,
513 columns,
514 exec,
515 )
516 }
517 }
518
519 fn execute_axis0_columns_uninit<T>(
520 &self,
521 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
522 operand: &RawStridedRef<'_, T>,
523 fill: T,
524 columns: &ContiguousPadAxis0Columns,
525 exec: Exec<'_>,
526 ) -> Result<()>
527 where
528 T: Copy + Send + Sync,
529 {
530 let dest_offset = dest.offset();
531 let dest_ptr = dest.data_mut().as_mut_ptr().cast::<T>();
532 let operand_offset = operand.offset();
533 let operand_ptr = operand.data().as_ptr();
534 unsafe {
537 self.write_axis0_columns_raw::<T, true>(
538 dest_ptr,
539 dest_offset,
540 operand_ptr,
541 operand_offset,
542 fill,
543 columns,
544 exec,
545 )
546 }
547 }
548
549 #[allow(clippy::too_many_arguments)]
567 unsafe fn write_axis0_columns_raw<T, const UNINIT: bool>(
568 &self,
569 dest_ptr: *mut T,
570 dest_offset: isize,
571 operand_ptr: *const T,
572 operand_offset: isize,
573 fill: T,
574 columns: &ContiguousPadAxis0Columns,
575 exec: Exec<'_>,
576 ) -> Result<()>
577 where
578 T: Copy + Send + Sync,
579 {
580 let column_total = columns.column_total;
581 if column_total == 0 {
582 return Ok(());
583 }
584 let exec = crate::threading::for_len(exec, self.dest_total);
585 {
586 if exec.budget().get() > 1 && column_total >= exec.budget().get() {
587 let dest_ptr = crate::threading::SendPtr(dest_ptr);
588 let operand_ptr = crate::threading::SendPtr(operand_ptr as *mut T);
589 return crate::threading::parallel_map_reduce(
590 0..column_total,
591 exec,
592 &|range| {
593 let mut outer_idx_storage =
594 CoordScratch::new(columns.dest_outer_dims.len());
595 let outer_idx = outer_idx_storage.as_mut_slice();
596 fill_col_major_index(range.start, &columns.dest_outer_dims, outer_idx);
597 unsafe {
603 self.write_axis0_columns_range::<T, UNINIT>(
604 dest_ptr.as_ptr(),
605 dest_offset,
606 operand_ptr.as_const(),
607 operand_offset,
608 fill,
609 columns,
610 outer_idx,
611 range.start,
612 range.len(),
613 Exec::serial(),
614 )
615 }
616 },
617 &|left, right| left.and(right),
618 );
619 }
620 }
621 let mut outer_idx_storage = CoordScratch::new(columns.dest_outer_dims.len());
622 let outer_idx = outer_idx_storage.as_mut_slice();
623 unsafe {
625 self.write_axis0_columns_range::<T, UNINIT>(
626 dest_ptr,
627 dest_offset,
628 operand_ptr,
629 operand_offset,
630 fill,
631 columns,
632 outer_idx,
633 0,
634 column_total,
635 exec,
636 )
637 }
638 }
639
640 #[allow(clippy::too_many_arguments)]
648 unsafe fn write_axis0_columns_range<T, const UNINIT: bool>(
649 &self,
650 dest_ptr: *mut T,
651 dest_offset: isize,
652 operand_ptr: *const T,
653 operand_offset: isize,
654 fill: T,
655 columns: &ContiguousPadAxis0Columns,
656 outer_idx: &mut [usize],
657 column_start: usize,
658 count: usize,
659 mut exec: Exec<'_>,
660 ) -> Result<()>
661 where
662 T: Copy + Send + Sync,
663 {
664 if count == 0 {
665 return Ok(());
666 }
667 let mut dest_column_offset =
668 checked_offset_add(dest_offset, columns.dest_axis0_step, column_start)?;
669 let mut operand_column_offset = operand_offset;
674 let mut out_of_bounds = 0usize;
675 for (axis, &coord) in outer_idx.iter().enumerate() {
676 match in_bounds_outer_coord(columns, axis, coord) {
677 Some(q) => {
678 operand_column_offset = checked_offset_add(
679 operand_column_offset,
680 columns.operand_outer_strides[axis],
681 q,
682 )?;
683 }
684 None => out_of_bounds += 1,
685 }
686 }
687 for _ in 0..count {
688 if out_of_bounds == 0 {
689 let low_len = columns.low0;
690 let run_len = columns.run_len;
691 let high_len = columns.high0;
692 unsafe {
699 if UNINIT {
700 fill_raw_span_uninit(
701 dest_ptr.offset(dest_column_offset),
702 low_len,
703 fill,
704 exec.reborrow(),
705 );
706 } else {
707 fill_raw_span(
708 dest_ptr.offset(dest_column_offset),
709 low_len,
710 fill,
711 exec.reborrow(),
712 );
713 }
714 crate::threading::copy_contiguous(
715 operand_ptr.offset(operand_column_offset),
716 dest_ptr.offset(dest_column_offset + low_len as isize),
717 run_len,
718 exec.reborrow(),
719 );
720 if UNINIT {
721 fill_raw_span_uninit(
722 dest_ptr
723 .offset(dest_column_offset + low_len as isize + run_len as isize),
724 high_len,
725 fill,
726 exec.reborrow(),
727 );
728 } else {
729 fill_raw_span(
730 dest_ptr
731 .offset(dest_column_offset + low_len as isize + run_len as isize),
732 high_len,
733 fill,
734 exec.reborrow(),
735 );
736 }
737 }
738 } else {
739 unsafe {
742 if UNINIT {
743 fill_raw_span_uninit(
744 dest_ptr.offset(dest_column_offset),
745 columns.dest_axis0_len,
746 fill,
747 exec.reborrow(),
748 );
749 } else {
750 fill_raw_span(
751 dest_ptr.offset(dest_column_offset),
752 columns.dest_axis0_len,
753 fill,
754 exec.reborrow(),
755 );
756 }
757 }
758 }
759 dest_column_offset = dest_column_offset
762 .checked_add(columns.dest_axis0_step)
763 .ok_or(StridedError::OffsetOverflow)?;
764 for (axis, coord) in outer_idx.iter_mut().enumerate() {
765 let was_in_bounds = in_bounds_outer_coord(columns, axis, *coord);
766 if let Some(q) = was_in_bounds {
767 let q = isize::try_from(q).map_err(|_| StridedError::OffsetOverflow)?;
768 let scaled = columns.operand_outer_strides[axis]
769 .checked_mul(q)
770 .ok_or(StridedError::OffsetOverflow)?;
771 operand_column_offset = operand_column_offset
772 .checked_sub(scaled)
773 .ok_or(StridedError::OffsetOverflow)?;
774 }
775 *coord += 1;
776 let wrapped = *coord >= columns.dest_outer_dims[axis];
777 if wrapped {
778 *coord = 0;
779 }
780 let is_in_bounds = in_bounds_outer_coord(columns, axis, *coord);
781 if let Some(q) = is_in_bounds {
782 operand_column_offset = checked_offset_add(
783 operand_column_offset,
784 columns.operand_outer_strides[axis],
785 q,
786 )?;
787 }
788 match (was_in_bounds.is_some(), is_in_bounds.is_some()) {
789 (false, true) => out_of_bounds -= 1,
790 (true, false) => out_of_bounds += 1,
791 _ => {}
792 }
793 if !wrapped {
794 break;
795 }
796 }
797 }
798 Ok(())
799 }
800
801 unsafe fn copy_axis0_runs_raw<T>(
814 &self,
815 dest_ptr: *mut T,
816 dest_offset: isize,
817 operand: &RawStridedRef<'_, T>,
818 run: ContiguousPadAxis0Run,
819 exec: Exec<'_>,
820 ) -> Result<()>
821 where
822 T: Copy + Send + Sync,
823 {
824 if run.len == 0 {
825 return Ok(());
826 }
827 let outer_dims = &self.operand_dims[1..];
828 let outer_total = checked_total_len(outer_dims)?;
829 let copied = outer_total.saturating_mul(run.len);
830 let exec = crate::threading::for_len(exec, copied);
831 {
832 if exec.budget().get() > 1 && outer_total >= exec.budget().get() {
833 let dest_ptr = crate::threading::SendPtr(dest_ptr);
834 return crate::threading::parallel_map_reduce(
835 0..outer_total,
836 exec,
837 &|range| {
838 let mut outer_idx_storage = CoordScratch::new(outer_dims.len());
839 let outer_idx = outer_idx_storage.as_mut_slice();
840 fill_col_major_index(range.start, outer_dims, outer_idx);
841 unsafe {
845 self.copy_axis0_run_range(
846 dest_ptr.as_ptr(),
847 dest_offset,
848 operand,
849 run,
850 outer_idx,
851 range.len(),
852 Exec::serial(),
853 )
854 }
855 },
856 &|left, right| left.and(right),
857 );
858 }
859 }
860 let mut outer_idx_storage = CoordScratch::new(outer_dims.len());
861 let outer_idx = outer_idx_storage.as_mut_slice();
862 unsafe {
864 self.copy_axis0_run_range(
865 dest_ptr,
866 dest_offset,
867 operand,
868 run,
869 outer_idx,
870 outer_total,
871 exec,
872 )
873 }
874 }
875
876 #[allow(clippy::too_many_arguments)]
884 unsafe fn copy_axis0_run_range<T>(
885 &self,
886 dest_ptr: *mut T,
887 dest_offset: isize,
888 operand: &RawStridedRef<'_, T>,
889 run: ContiguousPadAxis0Run,
890 outer_idx: &mut [usize],
891 count: usize,
892 mut exec: Exec<'_>,
893 ) -> Result<()>
894 where
895 T: Copy + Send + Sync,
896 {
897 let outer_dims = &self.operand_dims[1..];
898 let operand_ptr = operand.data().as_ptr();
899 for _ in 0..count {
900 let mut operand_offset =
901 checked_offset_add(operand.offset(), self.operand_strides[0], run.operand_start)?;
902 let mut dest_run_offset =
903 checked_offset_add(dest_offset, self.dest_strides[0], run.dest_start)?;
904 let mut in_bounds = true;
905 for (outer_axis, &coord) in outer_idx.iter().enumerate() {
906 let axis = outer_axis + 1;
907 let out_pos = i128::from(self.edge_padding_low[axis])
908 + coord as i128 * i128::from(self.interior_step[axis]);
909 if out_pos < 0 || out_pos >= self.dest_dims[axis] as i128 {
910 in_bounds = false;
911 break;
912 }
913 operand_offset =
914 checked_offset_add(operand_offset, self.operand_strides[axis], coord)?;
915 dest_run_offset =
916 checked_offset_add(dest_run_offset, self.dest_strides[axis], out_pos as usize)?;
917 }
918 if in_bounds {
919 unsafe {
925 crate::threading::copy_contiguous(
926 operand_ptr.offset(operand_offset),
927 dest_ptr.offset(dest_run_offset),
928 run.len,
929 exec.reborrow(),
930 );
931 }
932 }
933 advance_col_major_index(outer_idx, outer_dims);
934 }
935 Ok(())
936 }
937
938 #[cfg(test)]
939 fn contiguous_axis0_run(&self) -> Option<(usize, usize, usize)> {
940 self.contiguous_axis0_run
941 .map(|run| (run.operand_start, run.dest_start, run.len))
942 }
943
944 #[cfg(test)]
945 fn has_contiguous_dest_fill(&self) -> bool {
946 self.contiguous_dest_fill
947 }
948
949 #[cfg(test)]
950 fn has_contiguous_axis0_columns(&self) -> bool {
951 self.contiguous_axis0_columns.is_some()
952 }
953
954 fn fill_dest_serial<T>(&self, dest: &mut RawStridedMut<'_, T>, fill: T) -> Result<()>
955 where
956 T: Copy,
957 {
958 let dest_ptr = dest.data_mut().as_mut_ptr();
959 let mut cursor = PadFillState::new(dest.offset(), &self.dest_dims, &self.generic_fill);
960 for _ in 0..self.dest_total {
961 unsafe {
962 *dest_ptr.offset(cursor.offset) = fill;
966 }
967 cursor.advance(&self.dest_dims, &self.generic_fill);
968 }
969 Ok(())
970 }
971
972 fn fill_dest_parallel<T>(
973 &self,
974 dest: &mut RawStridedMut<'_, T>,
975 fill: T,
976 exec: Exec<'_>,
977 ) -> Result<()>
978 where
979 T: Copy + Send + Sync,
980 {
981 let dest_offset_base = dest.offset();
982 let dest_ptr = crate::threading::SendPtr(dest.data_mut().as_mut_ptr());
983 crate::threading::parallel_map_reduce(
984 0..self.dest_total,
985 exec,
986 &|range| {
987 let mut cursor = PadFillState::decode(
988 range.start,
989 dest_offset_base,
990 &self.dest_dims,
991 &self.generic_fill,
992 )?;
993 let dest_ptr = dest_ptr.as_ptr();
994 for _ in range {
995 unsafe {
996 *dest_ptr.offset(cursor.offset) = fill;
1001 }
1002 cursor.advance(&self.dest_dims, &self.generic_fill);
1003 }
1004 Ok(())
1005 },
1006 &|left, right| left.and(right),
1007 )
1008 }
1009
1010 fn copy_operand<T>(
1011 &self,
1012 dest: &mut RawStridedMut<'_, T>,
1013 operand: &RawStridedRef<'_, T>,
1014 exec: Exec<'_>,
1015 ) -> Result<()>
1016 where
1017 T: Copy + Send + Sync,
1018 {
1019 let exec = crate::threading::for_len(exec, self.generic_copy.total);
1020 if !exec.is_serial() {
1021 return self.copy_operand_parallel(dest, operand, exec);
1022 }
1023 self.copy_operand_serial(dest, operand)
1024 }
1025
1026 fn copy_operand_uninit<T>(
1027 &self,
1028 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1029 operand: &RawStridedRef<'_, T>,
1030 exec: Exec<'_>,
1031 ) -> Result<()>
1032 where
1033 T: Copy + Send + Sync,
1034 {
1035 let exec = crate::threading::for_len(exec, self.generic_copy.total);
1036 if !exec.is_serial() {
1037 return self.copy_operand_uninit_parallel(dest, operand, exec);
1038 }
1039 self.copy_operand_uninit_serial(dest, operand)
1040 }
1041
1042 fn copy_operand_uninit_serial<T>(
1043 &self,
1044 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1045 operand: &RawStridedRef<'_, T>,
1046 ) -> Result<()>
1047 where
1048 T: Copy,
1049 {
1050 if self.generic_copy.total == 0 {
1051 return Ok(());
1052 }
1053 let operand_ptr = operand.data().as_ptr();
1054 let dest_ptr = dest.data_mut().as_mut_ptr();
1055 let source_base = operand
1056 .offset()
1057 .checked_add(self.generic_copy.source_base_delta)
1058 .ok_or(StridedError::OffsetOverflow)?;
1059 let dest_base = dest
1060 .offset()
1061 .checked_add(self.generic_copy.dest_base_delta)
1062 .ok_or(StridedError::OffsetOverflow)?;
1063 let mut cursor = PadCopyState::new(source_base, dest_base, &self.generic_copy);
1064 for _ in 0..self.generic_copy.total {
1065 unsafe {
1066 (*dest_ptr.offset(cursor.dest_offset))
1071 .write(*operand_ptr.offset(cursor.source_offset));
1072 }
1073 cursor.advance(&self.generic_copy);
1074 }
1075 Ok(())
1076 }
1077
1078 fn copy_operand_uninit_parallel<T>(
1079 &self,
1080 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1081 operand: &RawStridedRef<'_, T>,
1082 exec: Exec<'_>,
1083 ) -> Result<()>
1084 where
1085 T: Copy + Send + Sync,
1086 {
1087 let operand_base = operand
1088 .offset()
1089 .checked_add(self.generic_copy.source_base_delta)
1090 .ok_or(StridedError::OffsetOverflow)?;
1091 let operand_ptr = crate::threading::SendPtr(operand.data().as_ptr() as *mut T);
1092 let dest_base = dest
1093 .offset()
1094 .checked_add(self.generic_copy.dest_base_delta)
1095 .ok_or(StridedError::OffsetOverflow)?;
1096 let dest_ptr = crate::threading::SendPtr(dest.data_mut().as_mut_ptr());
1097 crate::threading::parallel_map_reduce(
1098 0..self.generic_copy.total,
1099 exec,
1100 &|range| {
1101 let mut cursor =
1102 PadCopyState::decode(range.start, operand_base, dest_base, &self.generic_copy)?;
1103 let operand_ptr = operand_ptr.as_const();
1104 let dest_ptr = dest_ptr.as_ptr();
1105
1106 for _ in range {
1107 unsafe {
1108 (*dest_ptr.offset(cursor.dest_offset))
1113 .write(*operand_ptr.offset(cursor.source_offset));
1114 }
1115 cursor.advance(&self.generic_copy);
1116 }
1117 Ok(())
1118 },
1119 &|left, right| left.and(right),
1120 )
1121 }
1122
1123 fn copy_operand_serial<T>(
1124 &self,
1125 dest: &mut RawStridedMut<'_, T>,
1126 operand: &RawStridedRef<'_, T>,
1127 ) -> Result<()>
1128 where
1129 T: Copy,
1130 {
1131 if self.generic_copy.total == 0 {
1132 return Ok(());
1133 }
1134 let operand_ptr = operand.data().as_ptr();
1135 let dest_ptr = dest.data_mut().as_mut_ptr();
1136 let source_base = operand
1137 .offset()
1138 .checked_add(self.generic_copy.source_base_delta)
1139 .ok_or(StridedError::OffsetOverflow)?;
1140 let dest_base = dest
1141 .offset()
1142 .checked_add(self.generic_copy.dest_base_delta)
1143 .ok_or(StridedError::OffsetOverflow)?;
1144 let mut cursor = PadCopyState::new(source_base, dest_base, &self.generic_copy);
1145 for _ in 0..self.generic_copy.total {
1146 unsafe {
1147 *dest_ptr.offset(cursor.dest_offset) = *operand_ptr.offset(cursor.source_offset);
1152 }
1153 cursor.advance(&self.generic_copy);
1154 }
1155 Ok(())
1156 }
1157
1158 fn copy_operand_parallel<T>(
1159 &self,
1160 dest: &mut RawStridedMut<'_, T>,
1161 operand: &RawStridedRef<'_, T>,
1162 exec: Exec<'_>,
1163 ) -> Result<()>
1164 where
1165 T: Copy + Send + Sync,
1166 {
1167 let operand_base = operand
1168 .offset()
1169 .checked_add(self.generic_copy.source_base_delta)
1170 .ok_or(StridedError::OffsetOverflow)?;
1171 let operand_ptr = crate::threading::SendPtr(operand.data().as_ptr() as *mut T);
1172 let dest_base = dest
1173 .offset()
1174 .checked_add(self.generic_copy.dest_base_delta)
1175 .ok_or(StridedError::OffsetOverflow)?;
1176 let dest_ptr = crate::threading::SendPtr(dest.data_mut().as_mut_ptr());
1177 crate::threading::parallel_map_reduce(
1178 0..self.generic_copy.total,
1179 exec,
1180 &|range| {
1181 let mut cursor =
1182 PadCopyState::decode(range.start, operand_base, dest_base, &self.generic_copy)?;
1183 let operand_ptr = operand_ptr.as_const();
1184 let dest_ptr = dest_ptr.as_ptr();
1185
1186 for _ in range {
1187 unsafe {
1188 *dest_ptr.offset(cursor.dest_offset) =
1193 *operand_ptr.offset(cursor.source_offset);
1194 }
1195 cursor.advance(&self.generic_copy);
1196 }
1197 Ok(())
1198 },
1199 &|left, right| left.and(right),
1200 )
1201 }
1202
1203 fn check_call<D, T>(
1204 &self,
1205 dest: &RawStridedMut<'_, D>,
1206 operand: &RawStridedRef<'_, T>,
1207 ) -> Result<()> {
1208 if operand.dims() != &self.operand_dims[..]
1209 || operand.strides() != &self.operand_strides[..]
1210 || dest.dims() != &self.dest_dims[..]
1211 || dest.strides() != &self.dest_strides[..]
1212 {
1213 return Err(StridedError::PlanLayoutMismatch);
1214 }
1215 Ok(())
1216 }
1217}
1218
1219impl ConcatenatePlan {
1220 pub fn compile(
1222 input_dims: &[&[usize]],
1223 input_strides: &[&[isize]],
1224 dest_dims: &[usize],
1225 dest_strides: &[isize],
1226 axis: usize,
1227 ) -> Result<Self> {
1228 if input_dims.is_empty() {
1229 return Err(StridedError::UnsupportedArity {
1230 arity: 0,
1231 max: usize::MAX,
1232 });
1233 }
1234 if input_dims.len() != input_strides.len() {
1235 return Err(StridedError::RankMismatch(
1236 input_strides.len(),
1237 input_dims.len(),
1238 ));
1239 }
1240
1241 let rank = input_dims[0].len();
1242 if dest_dims.len() != rank || dest_strides.len() != rank {
1243 return Err(StridedError::StrideLengthMismatch);
1244 }
1245 if axis >= rank {
1246 return Err(StridedError::InvalidAxis { axis, rank });
1247 }
1248 checked_total_len(dest_dims)?;
1249 if !crate::layout_check::is_injective_layout(dest_dims, dest_strides) {
1250 return Err(StridedError::NonInjectiveOutputLayout);
1251 }
1252
1253 let mut expected_dest_dims: AxisVec<usize> = input_dims[0].into();
1254 expected_dest_dims[axis] = 0;
1255 let mut stored_input_dims = crate::inline::OperandVec::with_capacity(input_dims.len());
1256 let mut stored_input_strides = crate::inline::OperandVec::with_capacity(input_dims.len());
1257 let mut dest_offset_deltas = crate::inline::OperandVec::with_capacity(input_dims.len());
1258 let mut copy_plans = crate::inline::OperandVec::with_capacity(input_dims.len());
1259 let mut segment_starts = crate::inline::OperandVec::with_capacity(input_dims.len() + 1);
1260 segment_starts.push(0usize);
1261 let mut axis_base = 0usize;
1262
1263 for (dims, strides) in input_dims.iter().zip(input_strides.iter()) {
1264 if dims.len() != rank {
1265 return Err(StridedError::RankMismatch(dims.len(), rank));
1266 }
1267 if strides.len() != rank {
1268 return Err(StridedError::StrideLengthMismatch);
1269 }
1270 let segment_total = checked_total_len(dims)?;
1271 let segment_end = segment_starts[segment_starts.len() - 1]
1272 .checked_add(segment_total)
1273 .ok_or(StridedError::OffsetOverflow)?;
1274 segment_starts.push(segment_end);
1275 for dim in 0..rank {
1276 if dim == axis {
1277 expected_dest_dims[axis] = expected_dest_dims[axis]
1278 .checked_add(dims[axis])
1279 .ok_or(StridedError::OffsetOverflow)?;
1280 } else if dims[dim] != input_dims[0][dim] {
1281 return Err(StridedError::ShapeMismatch(
1282 dims.to_vec(),
1283 input_dims[0].to_vec(),
1284 ));
1285 }
1286 }
1287 dest_offset_deltas.push(checked_offset_add(0, dest_strides[axis], axis_base)?);
1288 axis_base = axis_base
1289 .checked_add(dims[axis])
1290 .ok_or(StridedError::OffsetOverflow)?;
1291 copy_plans.push(CopyPlan::compile(dims, dest_strides, strides)?);
1292 stored_input_dims.push((*dims).into());
1293 stored_input_strides.push((*strides).into());
1294 }
1295
1296 if dest_dims != &expected_dest_dims[..] {
1297 return Err(StridedError::ShapeMismatch(
1298 dest_dims.to_vec(),
1299 expected_dest_dims.to_vec(),
1300 ));
1301 }
1302
1303 Ok(Self {
1304 input_dims: stored_input_dims,
1305 input_strides: stored_input_strides,
1306 dest_dims: dest_dims.into(),
1307 dest_strides: dest_strides.into(),
1308 dest_offset_deltas,
1309 segment_starts,
1310 copy_plans,
1311 })
1312 }
1313
1314 pub fn execute<T>(
1316 &self,
1317 dest: &mut RawStridedMut<'_, T>,
1318 inputs: &[RawStridedRef<'_, T>],
1319 exec: Exec<'_>,
1320 ) -> Result<()>
1321 where
1322 T: Copy + Send + Sync,
1323 {
1324 let mut exec = exec;
1325 self.check_dest_layout(dest)?;
1326 if inputs.len() != self.input_dims.len() {
1327 return Err(StridedError::RankMismatch(
1328 inputs.len(),
1329 self.input_dims.len(),
1330 ));
1331 }
1332 for (position, input) in inputs.iter().enumerate() {
1333 self.check_input_layout(position, input)?;
1334 self.segment_offset(position, dest.offset())?;
1335 }
1336 if self.try_execute_parallel(
1337 dest,
1338 inputs,
1339 |dst: &mut T, value| *dst = value,
1340 exec.reborrow(),
1341 )? {
1342 return Ok(());
1343 }
1344 for (position, input) in inputs.iter().enumerate() {
1345 self.execute_segment(position, dest, input, exec.reborrow())?;
1346 }
1347 Ok(())
1348 }
1349
1350 pub fn execute_uninit<T>(
1352 &self,
1353 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1354 inputs: &[RawStridedRef<'_, T>],
1355 exec: Exec<'_>,
1356 ) -> Result<()>
1357 where
1358 T: Copy + Send + Sync,
1359 {
1360 let mut exec = exec;
1361 self.check_dest_layout(dest)?;
1362 if inputs.len() != self.input_dims.len() {
1363 return Err(StridedError::RankMismatch(
1364 inputs.len(),
1365 self.input_dims.len(),
1366 ));
1367 }
1368 for (position, input) in inputs.iter().enumerate() {
1369 self.check_input_layout(position, input)?;
1370 self.segment_offset(position, dest.offset())?;
1371 }
1372 if self.try_execute_parallel(
1373 dest,
1374 inputs,
1375 |dst: &mut MaybeUninit<T>, value| {
1376 dst.write(value);
1377 },
1378 exec.reborrow(),
1379 )? {
1380 return Ok(());
1381 }
1382 for (position, input) in inputs.iter().enumerate() {
1383 self.execute_segment_uninit(position, dest, input, exec.reborrow())?;
1384 }
1385 Ok(())
1386 }
1387
1388 fn try_execute_parallel<D, T, Apply>(
1399 &self,
1400 dest: &mut RawStridedMut<'_, D>,
1401 inputs: &[RawStridedRef<'_, T>],
1402 apply: Apply,
1403 exec: Exec<'_>,
1404 ) -> Result<bool>
1405 where
1406 D: Copy + Send + Sync,
1407 T: Copy + Send + Sync,
1408 Apply: Fn(&mut D, T) + Send + Sync,
1409 {
1410 let total = self.segment_starts[self.segment_starts.len() - 1];
1411 let exec = crate::threading::for_len(exec, total);
1412 if exec.is_serial() {
1413 return Ok(false);
1414 }
1415 let mut dest_offsets: crate::inline::OperandVec<isize> =
1420 crate::inline::OperandVec::with_capacity(inputs.len());
1421 for position in 0..inputs.len() {
1422 if self.copy_plans[position].fused_layout().is_none() {
1423 return Ok(false);
1424 }
1425 dest_offsets.push(self.segment_offset(position, dest.offset())?);
1426 }
1427 let dest_ptr = crate::threading::SendPtr(dest.data_mut().as_mut_ptr());
1428 let starts = &self.segment_starts;
1429 let dest_offsets = &dest_offsets;
1430 crate::threading::parallel_for_each(0..total, exec, &|range| {
1431 let mut position = starts[1..].partition_point(|&end| end <= range.start);
1433 while position < inputs.len() && starts[position] < range.end {
1434 let local_start = range.start.max(starts[position]) - starts[position];
1435 let local_end = range.end.min(starts[position + 1]) - starts[position];
1436 let Some(layout) = self.copy_plans[position].fused_layout() else {
1440 position += 1;
1441 continue;
1442 };
1443 let input = &inputs[position];
1444 unsafe {
1454 crate::raw_ops::apply_fused_range(
1455 dest_ptr.as_ptr(),
1456 dest_offsets[position],
1457 input.data().as_ptr(),
1458 input.offset(),
1459 layout,
1460 local_start,
1461 local_end - local_start,
1462 &apply,
1463 &|value| value,
1464 );
1465 }
1466 position += 1;
1467 }
1468 });
1469 Ok(true)
1470 }
1471
1472 pub(crate) fn check_dest_layout<T>(&self, dest: &RawStridedMut<'_, T>) -> Result<()> {
1473 if dest.dims() != &self.dest_dims[..] || dest.strides() != &self.dest_strides[..] {
1474 return Err(StridedError::PlanLayoutMismatch);
1475 }
1476 Ok(())
1477 }
1478
1479 pub(crate) fn check_input_layout<T>(
1480 &self,
1481 position: usize,
1482 input: &RawStridedRef<'_, T>,
1483 ) -> Result<()> {
1484 if position >= self.input_dims.len()
1485 || input.dims() != &self.input_dims[position][..]
1486 || input.strides() != &self.input_strides[position][..]
1487 {
1488 return Err(StridedError::PlanLayoutMismatch);
1489 }
1490 Ok(())
1491 }
1492
1493 pub(crate) fn input_count(&self) -> usize {
1494 self.input_dims.len()
1495 }
1496
1497 pub(crate) fn prefers_whole_plan(&self, exec: &Exec<'_>) -> bool {
1501 let total = self.segment_starts[self.segment_starts.len() - 1];
1502 crate::threading::is_parallel(total, exec)
1503 }
1504
1505 pub(crate) fn segment_offset(&self, position: usize, dest_offset: isize) -> Result<isize> {
1506 dest_offset
1507 .checked_add(self.dest_offset_deltas[position])
1508 .ok_or(StridedError::OffsetOverflow)
1509 }
1510
1511 pub(crate) fn execute_segment<T>(
1512 &self,
1513 position: usize,
1514 dest: &mut RawStridedMut<'_, T>,
1515 input: &RawStridedRef<'_, T>,
1516 exec: Exec<'_>,
1517 ) -> Result<()>
1518 where
1519 T: Copy + Send + Sync,
1520 {
1521 let segment_offset = self.segment_offset(position, dest.offset())?;
1522 let dest_data = dest.data_mut();
1523 let mut segment = unsafe {
1524 RawStridedMut::new_unchecked(
1525 dest_data,
1526 &self.input_dims[position],
1527 &self.dest_strides,
1528 segment_offset,
1529 )
1530 };
1531 self.copy_plans[position].execute(&mut segment, input, exec)
1532 }
1533
1534 pub(crate) fn execute_segment_uninit<T>(
1535 &self,
1536 position: usize,
1537 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1538 input: &RawStridedRef<'_, T>,
1539 exec: Exec<'_>,
1540 ) -> Result<()>
1541 where
1542 T: Copy + Send + Sync,
1543 {
1544 let segment_offset = self.segment_offset(position, dest.offset())?;
1545 let dest_data = dest.data_mut();
1546 let mut segment = unsafe {
1547 RawStridedMut::new_unchecked(
1548 dest_data,
1549 &self.input_dims[position],
1550 &self.dest_strides,
1551 segment_offset,
1552 )
1553 };
1554 self.copy_plans[position].execute_uninit(&mut segment, input, exec)
1555 }
1556}
1557
1558impl ReversePlan {
1559 pub fn compile(
1561 operand_dims: &[usize],
1562 operand_strides: &[isize],
1563 dest_strides: &[isize],
1564 axes: &[usize],
1565 ) -> Result<Self> {
1566 let rank = operand_dims.len();
1567 if operand_strides.len() != rank || dest_strides.len() != rank {
1568 return Err(StridedError::StrideLengthMismatch);
1569 }
1570 checked_total_len(operand_dims)?;
1571
1572 let mut reverse_axis: AxisVec<bool> = (0..rank).map(|_| false).collect();
1573 for &axis in axes {
1574 if axis >= rank {
1575 return Err(StridedError::InvalidAxis { axis, rank });
1576 }
1577 reverse_axis[axis] = true;
1578 }
1579
1580 let mut source_strides: AxisVec<isize> = AxisVec::with_capacity(rank);
1581 let mut source_offset_delta = 0isize;
1582 for axis in 0..rank {
1583 if reverse_axis[axis] {
1584 source_strides.push(
1585 operand_strides[axis]
1586 .checked_neg()
1587 .ok_or(StridedError::OffsetOverflow)?,
1588 );
1589 if operand_dims[axis] > 0 {
1590 source_offset_delta = checked_offset_add(
1591 source_offset_delta,
1592 operand_strides[axis],
1593 operand_dims[axis] - 1,
1594 )?;
1595 }
1596 } else {
1597 source_strides.push(operand_strides[axis]);
1598 }
1599 }
1600 let copy_plan = CopyPlan::compile(operand_dims, dest_strides, &source_strides)?;
1601
1602 Ok(Self {
1603 operand_dims: operand_dims.into(),
1604 operand_strides: operand_strides.into(),
1605 dest_strides: dest_strides.into(),
1606 source_strides,
1607 source_offset_delta,
1608 copy_plan,
1609 })
1610 }
1611
1612 pub fn execute<T>(
1614 &self,
1615 dest: &mut RawStridedMut<'_, T>,
1616 operand: &RawStridedRef<'_, T>,
1617 exec: Exec<'_>,
1618 ) -> Result<()>
1619 where
1620 T: Copy + Send + Sync,
1621 {
1622 self.check_call(dest, operand)?;
1623 let source_offset = operand
1624 .offset()
1625 .checked_add(self.source_offset_delta)
1626 .ok_or(StridedError::OffsetOverflow)?;
1627 let source = unsafe {
1628 RawStridedRef::new_unchecked(
1629 operand.data(),
1630 &self.operand_dims,
1631 &self.source_strides,
1632 source_offset,
1633 )
1634 };
1635 self.copy_plan.execute(dest, &source, exec)
1636 }
1637
1638 pub fn execute_uninit<T>(
1640 &self,
1641 dest: &mut RawStridedMut<'_, MaybeUninit<T>>,
1642 operand: &RawStridedRef<'_, T>,
1643 exec: Exec<'_>,
1644 ) -> Result<()>
1645 where
1646 T: Copy + Send + Sync,
1647 {
1648 self.check_call(dest, operand)?;
1649 let source_offset = operand
1650 .offset()
1651 .checked_add(self.source_offset_delta)
1652 .ok_or(StridedError::OffsetOverflow)?;
1653 let source = unsafe {
1654 RawStridedRef::new_unchecked(
1655 operand.data(),
1656 &self.operand_dims,
1657 &self.source_strides,
1658 source_offset,
1659 )
1660 };
1661 self.copy_plan.execute_uninit(dest, &source, exec)
1662 }
1663
1664 fn check_call<D, T>(
1665 &self,
1666 dest: &RawStridedMut<'_, D>,
1667 operand: &RawStridedRef<'_, T>,
1668 ) -> Result<()> {
1669 if operand.dims() != &self.operand_dims[..]
1670 || operand.strides() != &self.operand_strides[..]
1671 || dest.dims() != &self.operand_dims[..]
1672 || dest.strides() != &self.dest_strides[..]
1673 {
1674 return Err(StridedError::PlanLayoutMismatch);
1675 }
1676 Ok(())
1677 }
1678}
1679
1680fn checked_total_len(dims: &[usize]) -> Result<usize> {
1681 if dims.is_empty() {
1682 return Ok(1);
1683 }
1684 dims.iter()
1685 .try_fold(1usize, |acc, &dim| acc.checked_mul(dim))
1686 .ok_or(StridedError::OffsetOverflow)
1687}
1688
1689fn checked_stride_mul(stride: isize, factor: usize) -> Result<isize> {
1690 let factor = isize::try_from(factor).map_err(|_| StridedError::OffsetOverflow)?;
1691 stride
1692 .checked_mul(factor)
1693 .ok_or(StridedError::OffsetOverflow)
1694}
1695
1696fn checked_pad_output_dim(
1697 input_extent: usize,
1698 edge_low: i64,
1699 edge_high: i64,
1700 interior_step: i64,
1701 axis: usize,
1702 rank: usize,
1703) -> Result<usize> {
1704 let base = if input_extent == 0 {
1705 0i128
1706 } else {
1707 (input_extent as i128 - 1)
1708 .checked_mul(i128::from(interior_step))
1709 .and_then(|value| value.checked_add(1))
1710 .ok_or(StridedError::OffsetOverflow)?
1711 };
1712 let dim = i128::from(edge_low)
1713 .checked_add(i128::from(edge_high))
1714 .and_then(|value| value.checked_add(base))
1715 .ok_or(StridedError::OffsetOverflow)?;
1716 usize::try_from(dim).map_err(|_| StridedError::InvalidAxis { axis, rank })
1717}
1718
1719fn compile_pad_fill_cursor(dims: &[usize], strides: &[isize]) -> Result<PadFillCursor> {
1720 let mut steps = AxisVec::with_capacity(dims.len());
1721 let mut resets = AxisVec::with_capacity(dims.len());
1722 for (&dim, &stride) in dims.iter().zip(strides) {
1723 steps.push(stride);
1724 resets.push(checked_cursor_reset(stride, dim)?);
1725 }
1726 check_offset_span(dims, &steps)?;
1727 Ok(PadFillCursor { steps, resets })
1728}
1729
1730fn compile_pad_copy_cursor(
1731 operand_dims: &[usize],
1732 operand_strides: &[isize],
1733 dest_dims: &[usize],
1734 dest_strides: &[isize],
1735 edge_padding_low: &[i64],
1736 interior_step: &[i64],
1737) -> Result<PadCopyCursor> {
1738 let mut shape = AxisVec::with_capacity(operand_dims.len());
1739 let mut source_steps = AxisVec::with_capacity(operand_dims.len());
1740 let mut source_resets = AxisVec::with_capacity(operand_dims.len());
1741 let mut dest_steps = AxisVec::with_capacity(operand_dims.len());
1742 let mut dest_resets = AxisVec::with_capacity(operand_dims.len());
1743 let mut source_base_delta = 0isize;
1744 let mut dest_base_delta = 0isize;
1745 let mut copy_empty = false;
1746
1747 for axis in 0..operand_dims.len() {
1748 let (start, end) = checked_pad_valid_interval(
1749 operand_dims[axis],
1750 dest_dims[axis],
1751 edge_padding_low[axis],
1752 interior_step[axis],
1753 )?;
1754 let extent = end - start;
1755 shape.push(extent);
1756 if !copy_empty && extent != 0 {
1757 source_base_delta =
1758 checked_offset_add(source_base_delta, operand_strides[axis], start)?;
1759 let output_start = i128::from(edge_padding_low[axis])
1760 .checked_add(
1761 i128::try_from(start)
1762 .map_err(|_| StridedError::OffsetOverflow)?
1763 .checked_mul(i128::from(interior_step[axis]))
1764 .ok_or(StridedError::OffsetOverflow)?,
1765 )
1766 .ok_or(StridedError::OffsetOverflow)?;
1767 let output_start =
1768 usize::try_from(output_start).map_err(|_| StridedError::OffsetOverflow)?;
1769 dest_base_delta =
1770 checked_offset_add(dest_base_delta, dest_strides[axis], output_start)?;
1771 }
1772 copy_empty |= extent == 0;
1773
1774 let source_step = operand_strides[axis];
1775 let dest_step = checked_stride_mul_i64(dest_strides[axis], interior_step[axis])?;
1776 source_steps.push(source_step);
1777 source_resets.push(checked_cursor_reset(source_step, extent)?);
1778 dest_steps.push(dest_step);
1779 dest_resets.push(checked_cursor_reset(dest_step, extent)?);
1780 }
1781
1782 check_offset_span(&shape, &source_steps)?;
1783 check_offset_span(&shape, &dest_steps)?;
1784 let total = if shape.iter().any(|&extent| extent == 0) {
1785 0
1786 } else {
1787 checked_total_len(&shape)?
1788 };
1789
1790 Ok(PadCopyCursor {
1791 shape,
1792 source_base_delta,
1793 dest_base_delta,
1794 source_steps,
1795 source_resets,
1796 dest_steps,
1797 dest_resets,
1798 total,
1799 })
1800}
1801
1802fn checked_pad_valid_interval(
1803 input_extent: usize,
1804 dest_extent: usize,
1805 edge_low: i64,
1806 step: i64,
1807) -> Result<(usize, usize)> {
1808 if input_extent == 0 || dest_extent == 0 {
1809 return Ok((0, 0));
1810 }
1811 let step = i128::from(step);
1812 let lower = ceil_div_positive(-i128::from(edge_low), step)?;
1813 let dest_last = i128::try_from(dest_extent)
1814 .map_err(|_| StridedError::OffsetOverflow)?
1815 .checked_sub(1)
1816 .ok_or(StridedError::OffsetOverflow)?;
1817 let upper = floor_div_positive(
1818 dest_last
1819 .checked_sub(i128::from(edge_low))
1820 .ok_or(StridedError::OffsetOverflow)?,
1821 step,
1822 )?
1823 .checked_add(1)
1824 .ok_or(StridedError::OffsetOverflow)?;
1825 let input_extent = i128::try_from(input_extent).map_err(|_| StridedError::OffsetOverflow)?;
1826 let lower = lower.clamp(0, input_extent);
1827 let upper = upper.clamp(0, input_extent);
1828 if lower >= upper {
1829 return Ok((0, 0));
1830 }
1831 Ok((
1832 usize::try_from(lower).map_err(|_| StridedError::OffsetOverflow)?,
1833 usize::try_from(upper).map_err(|_| StridedError::OffsetOverflow)?,
1834 ))
1835}
1836
1837fn ceil_div_positive(numerator: i128, denominator: i128) -> Result<i128> {
1838 if denominator <= 0 {
1839 return Err(StridedError::OffsetOverflow);
1840 }
1841 let quotient = numerator.div_euclid(denominator);
1842 let remainder = numerator.rem_euclid(denominator);
1843 quotient
1844 .checked_add(i128::from(remainder != 0))
1845 .ok_or(StridedError::OffsetOverflow)
1846}
1847
1848fn floor_div_positive(numerator: i128, denominator: i128) -> Result<i128> {
1849 if denominator <= 0 {
1850 return Err(StridedError::OffsetOverflow);
1851 }
1852 Ok(numerator.div_euclid(denominator))
1853}
1854
1855fn checked_stride_mul_i64(stride: isize, factor: i64) -> Result<isize> {
1856 let factor = isize::try_from(factor).map_err(|_| StridedError::OffsetOverflow)?;
1857 stride
1858 .checked_mul(factor)
1859 .ok_or(StridedError::OffsetOverflow)
1860}
1861
1862fn checked_cursor_reset(step: isize, extent: usize) -> Result<isize> {
1863 if extent == 0 {
1864 return Ok(0);
1865 }
1866 let last = isize::try_from(extent - 1).map_err(|_| StridedError::OffsetOverflow)?;
1867 step.checked_mul(last)
1868 .and_then(isize::checked_neg)
1869 .ok_or(StridedError::OffsetOverflow)
1870}
1871
1872fn check_offset_span(shape: &[usize], strides: &[isize]) -> Result<()> {
1873 let mut min = 0isize;
1874 let mut max = 0isize;
1875 for (&extent, &stride) in shape.iter().zip(strides) {
1876 if extent <= 1 {
1877 continue;
1878 }
1879 let last = isize::try_from(extent - 1).map_err(|_| StridedError::OffsetOverflow)?;
1880 let delta = stride
1881 .checked_mul(last)
1882 .ok_or(StridedError::OffsetOverflow)?;
1883 if delta < 0 {
1884 min = min.checked_add(delta).ok_or(StridedError::OffsetOverflow)?;
1885 } else {
1886 max = max.checked_add(delta).ok_or(StridedError::OffsetOverflow)?;
1887 }
1888 }
1889 let _ = (min, max);
1890 Ok(())
1891}
1892
1893fn is_dense_col_major(dims: &[usize], strides: &[isize]) -> bool {
1894 let mut expected = 1isize;
1895 for (&dim, &stride) in dims.iter().zip(strides.iter()) {
1896 if stride != expected {
1897 return false;
1898 }
1899 let Ok(dim) = isize::try_from(dim) else {
1900 return false;
1901 };
1902 let Some(next) = expected.checked_mul(dim) else {
1903 return false;
1904 };
1905 expected = next;
1906 }
1907 true
1908}
1909
1910fn compile_contiguous_pad_axis0_run(
1911 operand_dims: &[usize],
1912 operand_strides: &[isize],
1913 dest_dims: &[usize],
1914 dest_strides: &[isize],
1915 edge_padding_low: &[i64],
1916 interior_step: &[i64],
1917) -> Option<ContiguousPadAxis0Run> {
1918 if operand_dims.is_empty()
1919 || operand_strides[0] != 1
1920 || dest_strides[0] != 1
1921 || interior_step[0] != 1
1922 {
1923 return None;
1924 }
1925
1926 let operand_extent = operand_dims[0] as i128;
1927 let dest_extent = dest_dims[0] as i128;
1928 let edge_low = i128::from(edge_padding_low[0]);
1929 let operand_start = (-edge_low).clamp(0, operand_extent);
1930 let dest_start = edge_low.clamp(0, dest_extent);
1931 let len = (operand_extent - operand_start).min(dest_extent - dest_start);
1932 Some(ContiguousPadAxis0Run {
1933 operand_start: usize::try_from(operand_start).ok()?,
1934 dest_start: usize::try_from(dest_start).ok()?,
1935 len: usize::try_from(len).ok()?,
1936 })
1937}
1938
1939#[allow(clippy::too_many_arguments)]
1942fn compile_contiguous_pad_axis0_columns(
1943 operand_dims: &[usize],
1944 operand_strides: &[isize],
1945 dest_dims: &[usize],
1946 edge_padding_low: &[i64],
1947 edge_padding_high: &[i64],
1948 interior_step: &[i64],
1949 contiguous_dest_fill: bool,
1950 run: ContiguousPadAxis0Run,
1951) -> Option<ContiguousPadAxis0Columns> {
1952 if run.len == 0 || !contiguous_dest_fill || dest_dims.is_empty() {
1953 return None;
1954 }
1955 if edge_padding_low.iter().any(|&low| low < 0)
1956 || edge_padding_high.iter().any(|&high| high < 0)
1957 || interior_step.iter().any(|&step| step != 1)
1958 {
1959 return None;
1960 }
1961
1962 let rank = operand_dims.len();
1963 let dest_axis0_len = dest_dims[0];
1964 let dest_axis0_step = isize::try_from(dest_axis0_len).ok()?;
1965 let column_total = checked_total_len(&dest_dims[1..]).ok()?;
1966 let mut operand_outer_dims = AxisVec::with_capacity(rank - 1);
1967 let mut operand_outer_strides = AxisVec::with_capacity(rank - 1);
1968 let mut dest_outer_dims = AxisVec::with_capacity(rank - 1);
1969 let mut edge_low_outer = AxisVec::with_capacity(rank - 1);
1970 for axis in 1..rank {
1971 operand_outer_dims.push(operand_dims[axis]);
1972 operand_outer_strides.push(operand_strides[axis]);
1973 dest_outer_dims.push(dest_dims[axis]);
1974 edge_low_outer.push(usize::try_from(edge_padding_low[axis]).ok()?);
1975 }
1976
1977 let low0 = run.dest_start;
1978 let high0 = dest_axis0_len.checked_sub(low0)?.checked_sub(run.len)?;
1979 Some(ContiguousPadAxis0Columns {
1980 column_total,
1981 dest_axis0_len,
1982 dest_axis0_step,
1983 low0,
1984 high0,
1985 run_len: run.len,
1986 operand_outer_dims,
1987 operand_outer_strides,
1988 dest_outer_dims,
1989 edge_low_outer,
1990 })
1991}
1992
1993unsafe fn fill_raw_span<T: Copy + Send + Sync>(ptr: *mut T, len: usize, fill: T, exec: Exec<'_>) {
2000 if len == 0 {
2001 return;
2002 }
2003 let span = unsafe { core::slice::from_raw_parts_mut(ptr, len) };
2006 crate::threading::fill_contiguous(span, fill, exec);
2007}
2008
2009unsafe fn fill_raw_span_uninit<T: Copy + Send + Sync>(
2016 ptr: *mut T,
2017 len: usize,
2018 fill: T,
2019 exec: Exec<'_>,
2020) {
2021 if len == 0 {
2022 return;
2023 }
2024 unsafe { crate::threading::fill_contiguous_uninit(ptr, len, fill, exec) };
2027}
2028
2029fn checked_offset_add(base: isize, stride: isize, coord: usize) -> Result<isize> {
2030 let coord = isize::try_from(coord).map_err(|_| StridedError::OffsetOverflow)?;
2031 let scaled = stride
2032 .checked_mul(coord)
2033 .ok_or(StridedError::OffsetOverflow)?;
2034 base.checked_add(scaled).ok_or(StridedError::OffsetOverflow)
2035}
2036
2037fn in_bounds_outer_coord(
2040 columns: &ContiguousPadAxis0Columns,
2041 axis: usize,
2042 coord: usize,
2043) -> Option<usize> {
2044 let q = coord.checked_sub(columns.edge_low_outer[axis])?;
2045 (q < columns.operand_outer_dims[axis]).then_some(q)
2046}
2047
2048fn advance_col_major_index(index: &mut [usize], shape: &[usize]) {
2049 for axis in 0..index.len() {
2050 index[axis] += 1;
2051 if index[axis] < shape[axis] {
2052 return;
2053 }
2054 index[axis] = 0;
2055 }
2056}
2057
2058fn fill_col_major_index(mut linear: usize, shape: &[usize], out: &mut [usize]) {
2059 for (axis, coord) in out.iter_mut().enumerate() {
2060 let dim = shape[axis];
2061 *coord = linear % dim;
2062 linear /= dim;
2063 }
2064}
2065
2066struct PadFillState {
2067 coords: CoordScratch,
2068 offset: isize,
2069}
2070
2071impl PadFillState {
2072 fn new(base: isize, shape: &[usize], _cursor: &PadFillCursor) -> Self {
2073 Self {
2074 coords: CoordScratch::new(shape.len()),
2075 offset: base,
2076 }
2077 }
2078
2079 fn decode(linear: usize, base: isize, shape: &[usize], cursor: &PadFillCursor) -> Result<Self> {
2080 let mut state = Self::new(base, shape, cursor);
2081 fill_col_major_index(linear, shape, state.coords.as_mut_slice());
2082 for (&coord, &step) in state.coords.as_mut_slice().iter().zip(&cursor.steps) {
2083 state.offset = checked_offset_add(state.offset, step, coord)?;
2084 }
2085 Ok(state)
2086 }
2087
2088 #[inline]
2089 fn advance(&mut self, shape: &[usize], cursor: &PadFillCursor) {
2090 for axis in 0..shape.len() {
2094 let next = self.coords.as_mut_slice()[axis] + 1;
2095 if next < shape[axis] {
2096 self.coords.as_mut_slice()[axis] = next;
2097 self.offset += cursor.steps[axis];
2098 return;
2099 }
2100 self.coords.as_mut_slice()[axis] = 0;
2101 self.offset += cursor.resets[axis];
2102 }
2103 }
2104}
2105
2106struct PadCopyState {
2107 coords: CoordScratch,
2108 source_offset: isize,
2109 dest_offset: isize,
2110}
2111
2112impl PadCopyState {
2113 fn new(source_base: isize, dest_base: isize, cursor: &PadCopyCursor) -> Self {
2114 Self {
2115 coords: CoordScratch::new(cursor.shape.len()),
2116 source_offset: source_base,
2117 dest_offset: dest_base,
2118 }
2119 }
2120
2121 fn decode(
2122 linear: usize,
2123 source_base: isize,
2124 dest_base: isize,
2125 cursor: &PadCopyCursor,
2126 ) -> Result<Self> {
2127 let mut state = Self::new(source_base, dest_base, cursor);
2128 fill_col_major_index(linear, &cursor.shape, state.coords.as_mut_slice());
2129 for axis in 0..cursor.shape.len() {
2130 let coord = state.coords.as_mut_slice()[axis];
2131 state.source_offset =
2132 checked_offset_add(state.source_offset, cursor.source_steps[axis], coord)?;
2133 state.dest_offset =
2134 checked_offset_add(state.dest_offset, cursor.dest_steps[axis], coord)?;
2135 }
2136 Ok(state)
2137 }
2138
2139 #[inline]
2140 fn advance(&mut self, cursor: &PadCopyCursor) {
2141 for axis in 0..cursor.shape.len() {
2145 let next = self.coords.as_mut_slice()[axis] + 1;
2146 if next < cursor.shape[axis] {
2147 self.coords.as_mut_slice()[axis] = next;
2148 self.source_offset += cursor.source_steps[axis];
2149 self.dest_offset += cursor.dest_steps[axis];
2150 return;
2151 }
2152 self.coords.as_mut_slice()[axis] = 0;
2153 self.source_offset += cursor.source_resets[axis];
2154 self.dest_offset += cursor.dest_resets[axis];
2155 }
2156 }
2157}
2158
2159struct CoordScratch {
2160 inline: [usize; crate::RAW_FUSED_RANK_LIMIT],
2161 heap: Option<Vec<usize>>,
2162 len: usize,
2163}
2164
2165impl CoordScratch {
2166 fn new(len: usize) -> Self {
2167 if len <= crate::RAW_FUSED_RANK_LIMIT {
2168 Self {
2169 inline: [0; crate::RAW_FUSED_RANK_LIMIT],
2170 heap: None,
2171 len,
2172 }
2173 } else {
2174 Self {
2175 inline: [0; crate::RAW_FUSED_RANK_LIMIT],
2176 heap: Some(vec![0; len]),
2177 len,
2178 }
2179 }
2180 }
2181
2182 fn as_mut_slice(&mut self) -> &mut [usize] {
2183 match &mut self.heap {
2184 Some(heap) => heap,
2185 None => &mut self.inline[..self.len],
2186 }
2187 }
2188}
2189
2190#[cfg(test)]
2191#[path = "static_indexing_plan/tests/tests.rs"]
2192mod tests;
2193
2194#[cfg(test)]
2195#[path = "static_indexing_plan/tests/parallel_tests.rs"]
2196mod parallel_tests;