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