1use super::{erased_raw_ref, map_output_dtype, raw_any, OneShotScalar};
11use crate::*;
12use core::mem::MaybeUninit;
13use core::num::Wrapping;
14use num_complex::{Complex32, Complex64};
15use strided_basic::execution::{
16 check_dtype, ensure_same_shape, map_raw_into_validated,
17 validate_destination_layout_without_alloc, validate_uninit_no_overlap,
18 zip_map2_raw_into_validated, ValidatedDestinationLayout,
19};
20
21pub fn erased_map_into_uninit(
58 input_dtype: KernelDType,
59 op: ErasedMapOp,
60 dest: &mut ErasedRawStridedUninitMut<'_>,
61 input: &ErasedRawStridedPtr<'_>,
62 exec: Exec<'_>,
63) -> Result<()> {
64 check_dtype(input_dtype, input.dtype())?;
65 check_dtype(map_output_dtype(input_dtype, op)?, dest.dtype())?;
66 validate_uninit_no_overlap(dest, input, 0)?;
67 let input = unsafe { input.try_as_ref_after_no_overlap() }?;
69
70 match (input_dtype, op) {
71 (KernelDType::C32, ErasedMapOp::Abs) => {
72 uninit_map_with::<f32, Complex32>(dest, &input, |value| value.norm(), exec)
73 }
74 (KernelDType::C64, ErasedMapOp::Abs) => {
75 uninit_map_with::<f64, Complex64>(dest, &input, |value| value.norm(), exec)
76 }
77 (KernelDType::F32, _) => uninit_map::<f32>(op, dest, &input, exec),
78 (KernelDType::F64, _) => uninit_map::<f64>(op, dest, &input, exec),
79 (KernelDType::I32, _) => uninit_map::<i32>(op, dest, &input, exec),
80 (KernelDType::I64, _) => uninit_map::<i64>(op, dest, &input, exec),
81 (KernelDType::Bool, _) => uninit_map::<bool>(op, dest, &input, exec),
82 (KernelDType::C32, _) => uninit_map::<Complex32>(op, dest, &input, exec),
83 (KernelDType::C64, _) => uninit_map::<Complex64>(op, dest, &input, exec),
84 _ => Err(StridedError::UnsupportedDType {
85 dtype: input_dtype.label(),
86 }),
87 }
88}
89
90pub fn erased_zip_into_uninit(
131 dtype: KernelDType,
132 op: ErasedZipOp,
133 dest: &mut ErasedRawStridedUninitMut<'_>,
134 lhs: &ErasedRawStridedPtr<'_>,
135 rhs: &ErasedRawStridedPtr<'_>,
136 exec: Exec<'_>,
137) -> Result<()> {
138 check_dtype(dtype, dest.dtype())?;
139 check_dtype(dtype, lhs.dtype())?;
140 check_dtype(dtype, rhs.dtype())?;
141 validate_uninit_no_overlap(dest, lhs, 0)?;
142 validate_uninit_no_overlap(dest, rhs, 1)?;
143 let lhs = unsafe { lhs.try_as_ref_after_no_overlap() }?;
145 let rhs = unsafe { rhs.try_as_ref_after_no_overlap() }?;
147
148 match dtype {
149 KernelDType::F32 => uninit_zip::<f32>(op, dest, &lhs, &rhs, exec),
150 KernelDType::F64 => uninit_zip::<f64>(op, dest, &lhs, &rhs, exec),
151 KernelDType::I32 => uninit_zip::<i32>(op, dest, &lhs, &rhs, exec),
152 KernelDType::I64 => uninit_zip::<i64>(op, dest, &lhs, &rhs, exec),
153 KernelDType::Bool => uninit_zip::<bool>(op, dest, &lhs, &rhs, exec),
154 KernelDType::C32 => uninit_zip::<Complex32>(op, dest, &lhs, &rhs, exec),
155 KernelDType::C64 => uninit_zip::<Complex64>(op, dest, &lhs, &rhs, exec),
156 _ => Err(StridedError::UnsupportedDType {
157 dtype: dtype.label(),
158 }),
159 }
160}
161
162pub fn erased_compare_into_uninit(
203 dtype: KernelDType,
204 op: CompareOp,
205 dest: &mut ErasedRawStridedUninitMut<'_>,
206 lhs: &ErasedRawStridedPtr<'_>,
207 rhs: &ErasedRawStridedPtr<'_>,
208 exec: Exec<'_>,
209) -> Result<()> {
210 check_dtype(KernelDType::Bool, dest.dtype())?;
211 check_dtype(dtype, lhs.dtype())?;
212 check_dtype(dtype, rhs.dtype())?;
213 validate_uninit_no_overlap(dest, lhs, 0)?;
214 validate_uninit_no_overlap(dest, rhs, 1)?;
215 let lhs = unsafe { lhs.try_as_ref_after_no_overlap() }?;
217 let rhs = unsafe { rhs.try_as_ref_after_no_overlap() }?;
219
220 match dtype {
221 KernelDType::F32 => uninit_compare_ordered::<f32>(op, dest, &lhs, &rhs, exec),
222 KernelDType::F64 => uninit_compare_ordered::<f64>(op, dest, &lhs, &rhs, exec),
223 KernelDType::I32 => uninit_compare_ordered::<i32>(op, dest, &lhs, &rhs, exec),
224 KernelDType::I64 => uninit_compare_ordered::<i64>(op, dest, &lhs, &rhs, exec),
225 KernelDType::Bool => uninit_compare_ordered::<bool>(op, dest, &lhs, &rhs, exec),
226 KernelDType::C32 => uninit_compare_eq::<Complex32>(op, dest, &lhs, &rhs, exec),
227 KernelDType::C64 => uninit_compare_eq::<Complex64>(op, dest, &lhs, &rhs, exec),
228 _ => Err(StridedError::UnsupportedDType {
229 dtype: dtype.label(),
230 }),
231 }
232}
233
234pub fn erased_select_into_uninit(
275 dtype: KernelDType,
276 dest: &mut ErasedRawStridedUninitMut<'_>,
277 pred: &ErasedRawStridedPtr<'_>,
278 on_true: &ErasedRawStridedPtr<'_>,
279 on_false: &ErasedRawStridedPtr<'_>,
280 exec: Exec<'_>,
281) -> Result<()> {
282 check_dtype(dtype, dest.dtype())?;
283 check_dtype(KernelDType::Bool, pred.dtype())?;
284 check_dtype(dtype, on_true.dtype())?;
285 check_dtype(dtype, on_false.dtype())?;
286 validate_uninit_no_overlap(dest, pred, 0)?;
287 validate_uninit_no_overlap(dest, on_true, 1)?;
288 validate_uninit_no_overlap(dest, on_false, 2)?;
289 let pred = unsafe { pred.try_as_ref_after_no_overlap() }?;
291 let on_true = unsafe { on_true.try_as_ref_after_no_overlap() }?;
293 let on_false = unsafe { on_false.try_as_ref_after_no_overlap() }?;
295
296 match dtype {
297 KernelDType::F32 => uninit_select::<f32>(dest, &pred, &on_true, &on_false, exec),
298 KernelDType::F64 => uninit_select::<f64>(dest, &pred, &on_true, &on_false, exec),
299 KernelDType::I32 => uninit_select::<i32>(dest, &pred, &on_true, &on_false, exec),
300 KernelDType::I64 => uninit_select::<i64>(dest, &pred, &on_true, &on_false, exec),
301 KernelDType::Bool => uninit_select::<bool>(dest, &pred, &on_true, &on_false, exec),
302 KernelDType::C32 => uninit_select::<Complex32>(dest, &pred, &on_true, &on_false, exec),
303 KernelDType::C64 => uninit_select::<Complex64>(dest, &pred, &on_true, &on_false, exec),
304 _ => Err(StridedError::UnsupportedDType {
305 dtype: dtype.label(),
306 }),
307 }
308}
309
310pub fn erased_clamp_into_uninit(
354 dtype: KernelDType,
355 dest: &mut ErasedRawStridedUninitMut<'_>,
356 x: &ErasedRawStridedPtr<'_>,
357 lo: &ErasedRawStridedPtr<'_>,
358 hi: &ErasedRawStridedPtr<'_>,
359 exec: Exec<'_>,
360) -> Result<()> {
361 check_dtype(dtype, dest.dtype())?;
362 check_dtype(dtype, x.dtype())?;
363 check_dtype(dtype, lo.dtype())?;
364 check_dtype(dtype, hi.dtype())?;
365 validate_uninit_no_overlap(dest, x, 0)?;
366 validate_uninit_no_overlap(dest, lo, 1)?;
367 validate_uninit_no_overlap(dest, hi, 2)?;
368 let x = unsafe { x.try_as_ref_after_no_overlap() }?;
370 let lo = unsafe { lo.try_as_ref_after_no_overlap() }?;
372 let hi = unsafe { hi.try_as_ref_after_no_overlap() }?;
374
375 match dtype {
376 KernelDType::F32 => uninit_clamp::<f32>(dest, &x, &lo, &hi, exec),
377 KernelDType::F64 => uninit_clamp::<f64>(dest, &x, &lo, &hi, exec),
378 KernelDType::I32 => uninit_clamp::<i32>(dest, &x, &lo, &hi, exec),
379 KernelDType::I64 => uninit_clamp::<i64>(dest, &x, &lo, &hi, exec),
380 _ => Err(StridedError::UnsupportedDType {
381 dtype: dtype.label(),
382 }),
383 }
384}
385
386pub fn erased_broadcast_mul_into_uninit(
431 dtype: KernelDType,
432 dest: &mut ErasedRawStridedUninitMut<'_>,
433 lhs: &ErasedRawStridedPtr<'_>,
434 lhs_axes: &[usize],
435 rhs: &ErasedRawStridedPtr<'_>,
436 rhs_axes: &[usize],
437 exec: Exec<'_>,
438) -> Result<()> {
439 check_dtype(dtype, dest.dtype())?;
440 check_dtype(dtype, lhs.dtype())?;
441 check_dtype(dtype, rhs.dtype())?;
442 validate_uninit_no_overlap(dest, lhs, 0)?;
443 validate_uninit_no_overlap(dest, rhs, 1)?;
444 let lhs = unsafe { lhs.try_as_ref_after_no_overlap() }?;
446 let rhs = unsafe { rhs.try_as_ref_after_no_overlap() }?;
448
449 match dtype {
450 KernelDType::F32 => uninit_broadcast_mul::<f32>(dest, &lhs, lhs_axes, &rhs, rhs_axes, exec),
451 KernelDType::F64 => uninit_broadcast_mul::<f64>(dest, &lhs, lhs_axes, &rhs, rhs_axes, exec),
452 KernelDType::C32 => {
453 uninit_broadcast_mul::<Complex32>(dest, &lhs, lhs_axes, &rhs, rhs_axes, exec)
454 }
455 KernelDType::C64 => {
456 uninit_broadcast_mul::<Complex64>(dest, &lhs, lhs_axes, &rhs, rhs_axes, exec)
457 }
458 KernelDType::I32 => {
459 uninit_broadcast_mul_wrapping::<i32>(dest, &lhs, lhs_axes, &rhs, rhs_axes, exec)
460 }
461 KernelDType::I64 => {
462 uninit_broadcast_mul_wrapping::<i64>(dest, &lhs, lhs_axes, &rhs, rhs_axes, exec)
463 }
464 _ => Err(StridedError::UnsupportedDType {
465 dtype: dtype.label(),
466 }),
467 }
468}
469
470fn prepare_destination(
474 dest: &ErasedRawStridedUninitMut<'_>,
475 inputs: &[&[usize]],
476) -> Result<Option<ValidatedDestinationLayout>> {
477 let validated = validate_destination_layout_without_alloc(dest.dims(), dest.strides())?;
478 for input_dims in inputs {
479 ensure_same_shape(dest.dims(), input_dims)?;
480 }
481 if dest.dims().contains(&0) {
482 Ok(None)
483 } else {
484 Ok(Some(validated))
485 }
486}
487
488fn uninit_raw_mut<'a, T: KernelStorageElement>(
489 dest: &'a mut ErasedRawStridedUninitMut<'_>,
490) -> Result<RawStridedMut<'a, MaybeUninit<T>>> {
491 let dims = dest.dims();
492 let strides = dest.strides();
493 let offset = dest.offset();
494 let data = dest.data_as_uninit_mut::<T>()?;
495 Ok(unsafe { RawStridedMut::new_unchecked(data, dims, strides, offset) })
498}
499
500fn uninit_view_mut<'a, T: KernelStorageElement>(
501 dest: &'a mut ErasedRawStridedUninitMut<'_>,
502) -> Result<StridedViewMut<'a, MaybeUninit<T>>> {
503 let dims = dest.dims();
504 let strides = dest.strides();
505 let offset = dest.offset();
506 let data = dest.data_as_uninit_mut::<T>()?;
507 Ok(unsafe { StridedViewMut::new_unchecked(data, dims, strides, offset) })
510}
511
512fn erased_view<'a, T: KernelStorageElement>(
513 src: &'a ErasedRawStridedRef<'a>,
514) -> Result<StridedView<'a, T>> {
515 let data = src.data_as::<T>()?;
516 Ok(unsafe { StridedView::new_unchecked(data, src.dims(), src.strides(), src.offset()) })
518}
519
520fn uninit_map<T: OneShotScalar>(
521 op: ErasedMapOp,
522 dest: &mut ErasedRawStridedUninitMut<'_>,
523 input: &ErasedRawStridedRef<'_>,
524 exec: Exec<'_>,
525) -> Result<()> {
526 if !T::supports_map(op) {
527 return Err(StridedError::UnsupportedOp {
528 op: op.label(),
529 dtype: T::one_shot_dtype_label(),
530 });
531 }
532 match op {
535 ErasedMapOp::Negate => uninit_map_with::<T, T>(
536 dest,
537 input,
538 |value| T::map(ErasedMapOp::Negate, value),
539 exec,
540 ),
541 ErasedMapOp::Conj => {
542 uninit_map_with::<T, T>(dest, input, |value| T::map(ErasedMapOp::Conj, value), exec)
543 }
544 ErasedMapOp::Abs => {
545 uninit_map_with::<T, T>(dest, input, |value| T::map(ErasedMapOp::Abs, value), exec)
546 }
547 ErasedMapOp::Sign => {
548 uninit_map_with::<T, T>(dest, input, |value| T::map(ErasedMapOp::Sign, value), exec)
549 }
550 }
551}
552
553fn uninit_map_with<D, A>(
554 dest: &mut ErasedRawStridedUninitMut<'_>,
555 input: &ErasedRawStridedRef<'_>,
556 map: impl Fn(A) -> D + Sync,
557 exec: Exec<'_>,
558) -> Result<()>
559where
560 D: Copy + Send + Sync + KernelStorageElement,
561 A: Copy + Send + Sync + KernelStorageElement,
562{
563 let Some(validated) = prepare_destination(dest, &[input.dims()])? else {
564 return Ok(());
565 };
566 let input = erased_raw_ref::<A>(input)?;
567 let mut dest = uninit_raw_mut::<D>(dest)?;
568 unsafe {
571 map_raw_into_validated::<MaybeUninit<D>, A, Identity>(
572 &mut dest,
573 &input,
574 |value| MaybeUninit::new(map(value)),
575 validated,
576 exec,
577 )
578 }
579}
580
581fn uninit_zip<T: OneShotScalar>(
582 op: ErasedZipOp,
583 dest: &mut ErasedRawStridedUninitMut<'_>,
584 lhs: &ErasedRawStridedRef<'_>,
585 rhs: &ErasedRawStridedRef<'_>,
586 exec: Exec<'_>,
587) -> Result<()> {
588 if !T::supports_zip(op) {
589 return Err(StridedError::UnsupportedOp {
590 op: op.label(),
591 dtype: T::one_shot_dtype_label(),
592 });
593 }
594 let Some(validated) = prepare_destination(dest, &[lhs.dims(), rhs.dims()])? else {
595 return Ok(());
596 };
597 let lhs = erased_raw_ref::<T>(lhs)?;
598 let rhs = erased_raw_ref::<T>(rhs)?;
599 if matches!(op, ErasedZipOp::Divide | ErasedZipOp::Remainder)
600 && T::INTEGER
601 && raw_any(&rhs, T::is_zero)?
602 {
603 return Err(StridedError::IntegerDivisionByZero { op: op.label() });
604 }
605 let mut dest = uninit_raw_mut::<T>(dest)?;
606 macro_rules! replay {
609 ($op:ident) => {
610 unsafe {
613 zip_map2_raw_into_validated::<MaybeUninit<T>, T, T, Identity, Identity>(
614 &mut dest,
615 &lhs,
616 &rhs,
617 |lhs, rhs| MaybeUninit::new(T::zip(ErasedZipOp::$op, lhs, rhs)),
618 validated,
619 exec,
620 )
621 }
622 };
623 }
624 match op {
625 ErasedZipOp::Add => replay!(Add),
626 ErasedZipOp::Subtract => replay!(Subtract),
627 ErasedZipOp::Multiply => replay!(Multiply),
628 ErasedZipOp::Divide => replay!(Divide),
629 ErasedZipOp::Remainder => replay!(Remainder),
630 ErasedZipOp::Maximum => replay!(Maximum),
631 ErasedZipOp::Minimum => replay!(Minimum),
632 }
633}
634
635fn uninit_compare_with<T>(
636 dest: &mut ErasedRawStridedUninitMut<'_>,
637 lhs: &ErasedRawStridedRef<'_>,
638 rhs: &ErasedRawStridedRef<'_>,
639 compare: impl Fn(T, T) -> bool + Sync,
640 exec: Exec<'_>,
641) -> Result<()>
642where
643 T: Copy + Send + Sync + KernelStorageElement,
644{
645 let Some(validated) = prepare_destination(dest, &[lhs.dims(), rhs.dims()])? else {
646 return Ok(());
647 };
648 let lhs = erased_raw_ref::<T>(lhs)?;
649 let rhs = erased_raw_ref::<T>(rhs)?;
650 let mut dest = uninit_raw_mut::<bool>(dest)?;
651 unsafe {
654 zip_map2_raw_into_validated::<MaybeUninit<bool>, T, T, Identity, Identity>(
655 &mut dest,
656 &lhs,
657 &rhs,
658 |lhs, rhs| MaybeUninit::new(compare(lhs, rhs)),
659 validated,
660 exec,
661 )
662 }
663}
664
665fn uninit_compare_ordered<T>(
666 op: CompareOp,
667 dest: &mut ErasedRawStridedUninitMut<'_>,
668 lhs: &ErasedRawStridedRef<'_>,
669 rhs: &ErasedRawStridedRef<'_>,
670 exec: Exec<'_>,
671) -> Result<()>
672where
673 T: Copy + Send + Sync + KernelStorageElement + PartialOrd,
674{
675 match op {
677 CompareOp::Eq => uninit_compare_with::<T>(dest, lhs, rhs, |a, b| a == b, exec),
678 CompareOp::Lt => uninit_compare_with::<T>(dest, lhs, rhs, |a, b| a < b, exec),
679 CompareOp::Le => uninit_compare_with::<T>(dest, lhs, rhs, |a, b| a <= b, exec),
680 CompareOp::Gt => uninit_compare_with::<T>(dest, lhs, rhs, |a, b| a > b, exec),
681 CompareOp::Ge => uninit_compare_with::<T>(dest, lhs, rhs, |a, b| a >= b, exec),
682 _ => Err(StridedError::UnsupportedOp {
683 op: compare_label(op),
684 dtype: T::DTYPE.label(),
685 }),
686 }
687}
688
689fn uninit_compare_eq<T>(
690 op: CompareOp,
691 dest: &mut ErasedRawStridedUninitMut<'_>,
692 lhs: &ErasedRawStridedRef<'_>,
693 rhs: &ErasedRawStridedRef<'_>,
694 exec: Exec<'_>,
695) -> Result<()>
696where
697 T: Copy + Send + Sync + KernelStorageElement + PartialEq,
698{
699 match op {
700 CompareOp::Eq => uninit_compare_with::<T>(dest, lhs, rhs, |a, b| a == b, exec),
701 _ => Err(StridedError::UnsupportedOp {
702 op: compare_label(op),
703 dtype: T::DTYPE.label(),
704 }),
705 }
706}
707
708fn compare_label(op: CompareOp) -> &'static str {
709 match op {
710 CompareOp::Eq => "eq",
711 CompareOp::Lt => "lt",
712 CompareOp::Le => "le",
713 CompareOp::Gt => "gt",
714 CompareOp::Ge => "ge",
715 _ => "compare",
716 }
717}
718
719fn uninit_select<T>(
720 dest: &mut ErasedRawStridedUninitMut<'_>,
721 pred: &ErasedRawStridedRef<'_>,
722 on_true: &ErasedRawStridedRef<'_>,
723 on_false: &ErasedRawStridedRef<'_>,
724 exec: Exec<'_>,
725) -> Result<()>
726where
727 T: Copy + Send + Sync + KernelStorageElement,
728{
729 let dims: [&[usize]; 3] = [pred.dims(), on_true.dims(), on_false.dims()];
730 if prepare_destination(dest, &dims)?.is_none() {
731 return Ok(());
732 }
733 let pred = erased_view::<bool>(pred)?;
734 let on_true = erased_view::<T>(on_true)?;
735 let on_false = erased_view::<T>(on_false)?;
736 let mut dest = uninit_view_mut::<T>(dest)?;
737 zip_map3_into(
738 &mut dest,
739 &pred,
740 &on_true,
741 &on_false,
742 |pred, a, b| MaybeUninit::new(if pred { a } else { b }),
743 exec,
744 )
745}
746
747fn uninit_clamp<T: OneShotScalar>(
748 dest: &mut ErasedRawStridedUninitMut<'_>,
749 x: &ErasedRawStridedRef<'_>,
750 lo: &ErasedRawStridedRef<'_>,
751 hi: &ErasedRawStridedRef<'_>,
752 exec: Exec<'_>,
753) -> Result<()> {
754 let dims: [&[usize]; 3] = [x.dims(), lo.dims(), hi.dims()];
755 if prepare_destination(dest, &dims)?.is_none() {
756 return Ok(());
757 }
758 let x = erased_view::<T>(x)?;
759 let lo = erased_view::<T>(lo)?;
760 let hi = erased_view::<T>(hi)?;
761 let mut dest = uninit_view_mut::<T>(dest)?;
762 zip_map3_into(
763 &mut dest,
764 &x,
765 &lo,
766 &hi,
767 |x, lo, hi| MaybeUninit::new(T::clamp(x, lo, hi)),
768 exec,
769 )
770}
771
772fn uninit_broadcast_mul<T>(
773 dest: &mut ErasedRawStridedUninitMut<'_>,
774 lhs: &ErasedRawStridedRef<'_>,
775 lhs_axes: &[usize],
776 rhs: &ErasedRawStridedRef<'_>,
777 rhs_axes: &[usize],
778 exec: Exec<'_>,
779) -> Result<()>
780where
781 T: Copy + Send + Sync + KernelStorageElement + core::ops::Mul<Output = T>,
782{
783 let lhs = erased_view::<T>(lhs)?;
784 let rhs = erased_view::<T>(rhs)?;
785 let mut dest = uninit_view_mut::<T>(dest)?;
786 broadcast_mul_into_uninit(&mut dest, &lhs, lhs_axes, &rhs, rhs_axes, exec)
787}
788
789fn uninit_broadcast_mul_wrapping<T>(
790 dest: &mut ErasedRawStridedUninitMut<'_>,
791 lhs: &ErasedRawStridedRef<'_>,
792 lhs_axes: &[usize],
793 rhs: &ErasedRawStridedRef<'_>,
794 rhs_axes: &[usize],
795 exec: Exec<'_>,
796) -> Result<()>
797where
798 T: Copy + Send + Sync + KernelStorageElement,
799 Wrapping<T>: core::ops::Mul<Output = Wrapping<T>> + Send + Sync,
800{
801 let lhs_data = wrapping_slice(lhs.data_as::<T>()?);
802 let rhs_data = wrapping_slice(rhs.data_as::<T>()?);
803 let lhs = unsafe {
806 StridedView::<Wrapping<T>, Identity>::new_unchecked(
807 lhs_data,
808 lhs.dims(),
809 lhs.strides(),
810 lhs.offset(),
811 )
812 };
813 let rhs = unsafe {
815 StridedView::<Wrapping<T>, Identity>::new_unchecked(
816 rhs_data,
817 rhs.dims(),
818 rhs.strides(),
819 rhs.offset(),
820 )
821 };
822 let dims = dest.dims();
823 let strides = dest.strides();
824 let offset = dest.offset();
825 let data = dest.data_as_uninit_mut::<T>()?;
826 let data = unsafe {
830 core::slice::from_raw_parts_mut(
831 data.as_mut_ptr().cast::<MaybeUninit<Wrapping<T>>>(),
832 data.len(),
833 )
834 };
835 let mut dest = unsafe { StridedViewMut::new_unchecked(data, dims, strides, offset) };
837 broadcast_mul_into_uninit(&mut dest, &lhs, lhs_axes, &rhs, rhs_axes, exec)
838}
839
840fn wrapping_slice<T>(data: &[T]) -> &[Wrapping<T>] {
841 unsafe { core::slice::from_raw_parts(data.as_ptr().cast::<Wrapping<T>>(), data.len()) }
844}