1use crate::*;
2use core::mem::MaybeUninit;
3use num_complex::{Complex32, Complex64};
4use strided_basic::execution::{
5 check_dtype, ensure_same_shape, erased_view, validate_destination_layout_without_alloc,
6 validate_uninit_no_overlap, ValidatedDestinationLayout,
7};
8const ERASED_FUSED_INPUT_LIMIT: usize = 4;
9
10#[derive(Clone, Debug)]
18pub struct ErasedFusedPlan {
19 dtype: KernelDType,
20 plan: FusedPlan,
21}
22
23impl ErasedFusedPlan {
24 pub fn compile(dtype: KernelDType, plan: FusedPlan) -> Result<Self> {
35 check_fused_dtype(dtype)?;
36 if plan.input_count == 0 || plan.input_count > ERASED_FUSED_INPUT_LIMIT {
37 return Err(StridedError::UnsupportedArity {
38 arity: plan.input_count,
39 max: ERASED_FUSED_INPUT_LIMIT,
40 });
41 }
42 validate_fused_plan_for_dtype(dtype, &plan)?;
43 Ok(Self { dtype, plan })
44 }
45
46 #[inline]
47 pub fn dtype(&self) -> KernelDType {
48 self.dtype
49 }
50
51 #[inline]
52 pub fn plan(&self) -> &FusedPlan {
53 &self.plan
54 }
55
56 pub fn execute(
66 &self,
67 dest: &mut ErasedRawStridedMut<'_>,
68 inputs: &[ErasedRawStridedRef<'_>],
69 exec: Exec<'_>,
70 ) -> Result<()> {
71 self.execute_outputs(std::slice::from_mut(dest), inputs, exec)
72 }
73
74 pub fn execute_outputs(
123 &self,
124 dests: &mut [ErasedRawStridedMut<'_>],
125 inputs: &[ErasedRawStridedRef<'_>],
126 exec: Exec<'_>,
127 ) -> Result<()> {
128 if inputs.len() != self.plan.input_count {
129 return Err(StridedError::RankMismatch(
130 inputs.len(),
131 self.plan.input_count,
132 ));
133 }
134 if dests.len() != self.plan.outputs.len() {
135 return Err(StridedError::RankMismatch(
136 dests.len(),
137 self.plan.outputs.len(),
138 ));
139 }
140 for dest in dests.iter() {
141 check_dtype(self.dtype, dest.dtype())?;
142 }
143 for input in inputs {
144 check_dtype(self.dtype, input.dtype())?;
145 }
146 let dims = dests[0].dims();
147 for dest in &dests[1..] {
148 ensure_same_shape(dims, dest.dims())?;
149 }
150 for input in inputs {
151 ensure_same_shape(dims, input.dims())?;
152 }
153
154 match self.dtype {
155 KernelDType::F32 => execute_fused_multi::<f32>(&self.plan, dests, inputs, exec),
156 KernelDType::F64 => execute_fused_multi::<f64>(&self.plan, dests, inputs, exec),
157 KernelDType::I32 => execute_fused_multi::<i32>(&self.plan, dests, inputs, exec),
158 KernelDType::I64 => execute_fused_multi::<i64>(&self.plan, dests, inputs, exec),
159 KernelDType::Bool => execute_fused_multi::<bool>(&self.plan, dests, inputs, exec),
160 KernelDType::C32 => execute_fused_multi::<Complex32>(&self.plan, dests, inputs, exec),
161 KernelDType::C64 => execute_fused_multi::<Complex64>(&self.plan, dests, inputs, exec),
162 _ => Err(StridedError::UnsupportedDType {
163 dtype: self.dtype.label(),
164 }),
165 }
166 }
167
168 pub fn execute_uninit(
180 &self,
181 dest: &mut ErasedRawStridedUninitMut<'_>,
182 inputs: &[ErasedRawStridedPtr<'_>],
183 exec: Exec<'_>,
184 ) -> Result<()> {
185 self.execute_outputs_uninit(std::slice::from_mut(dest), inputs, exec)
186 }
187
188 pub fn execute_outputs_uninit(
251 &self,
252 dests: &mut [ErasedRawStridedUninitMut<'_>],
253 inputs: &[ErasedRawStridedPtr<'_>],
254 exec: Exec<'_>,
255 ) -> Result<()> {
256 if inputs.len() != self.plan.input_count {
257 return Err(StridedError::RankMismatch(
258 inputs.len(),
259 self.plan.input_count,
260 ));
261 }
262 if dests.len() != self.plan.outputs.len() {
263 return Err(StridedError::RankMismatch(
264 dests.len(),
265 self.plan.outputs.len(),
266 ));
267 }
268 for dest in dests.iter() {
269 check_dtype(self.dtype, dest.dtype())?;
270 }
271 for input in inputs {
272 check_dtype(self.dtype, input.dtype())?;
273 }
274 let dims = dests[0].dims();
275 for dest in &dests[1..] {
276 ensure_same_shape(dims, dest.dims())?;
277 }
278 for input in inputs {
279 ensure_same_shape(dims, input.dims())?;
280 }
281 for (index, input) in inputs.iter().enumerate() {
282 for dest in dests.iter() {
283 validate_uninit_no_overlap(dest, input, index)?;
284 }
285 }
286 let mut validated: strided_basic::OperandVec<ValidatedDestinationLayout> =
290 strided_basic::OperandVec::with_capacity(dests.len());
291 for dest in dests.iter() {
292 validated.push(validate_destination_layout_without_alloc(
293 dest.dims(),
294 dest.strides(),
295 )?);
296 }
297 self.dispatch_uninit(dests, inputs, validated.as_slice(), exec)
298 }
299
300 fn dispatch_uninit(
301 &self,
302 dests: &mut [ErasedRawStridedUninitMut<'_>],
303 inputs: &[ErasedRawStridedPtr<'_>],
304 validated: &[ValidatedDestinationLayout],
305 exec: Exec<'_>,
306 ) -> Result<()> {
307 match self.dtype {
308 KernelDType::F32 => {
309 execute_fused_uninit_multi::<f32>(&self.plan, dests, inputs, validated, exec)
310 }
311 KernelDType::F64 => {
312 execute_fused_uninit_multi::<f64>(&self.plan, dests, inputs, validated, exec)
313 }
314 KernelDType::I32 => {
315 execute_fused_uninit_multi::<i32>(&self.plan, dests, inputs, validated, exec)
316 }
317 KernelDType::I64 => {
318 execute_fused_uninit_multi::<i64>(&self.plan, dests, inputs, validated, exec)
319 }
320 KernelDType::Bool => {
321 execute_fused_uninit_multi::<bool>(&self.plan, dests, inputs, validated, exec)
322 }
323 KernelDType::C32 => {
324 execute_fused_uninit_multi::<Complex32>(&self.plan, dests, inputs, validated, exec)
325 }
326 KernelDType::C64 => {
327 execute_fused_uninit_multi::<Complex64>(&self.plan, dests, inputs, validated, exec)
328 }
329 _ => Err(StridedError::UnsupportedDType {
330 dtype: self.dtype.label(),
331 }),
332 }
333 }
334}
335
336fn check_fused_dtype(dtype: KernelDType) -> Result<()> {
337 match dtype {
338 KernelDType::F32
339 | KernelDType::F64
340 | KernelDType::I32
341 | KernelDType::I64
342 | KernelDType::Bool
343 | KernelDType::C32
344 | KernelDType::C64 => Ok(()),
345 _ => Err(StridedError::UnsupportedDType {
346 dtype: dtype.label(),
347 }),
348 }
349}
350
351fn validate_fused_plan_for_dtype(dtype: KernelDType, plan: &FusedPlan) -> Result<()> {
352 let output_count = plan.outputs.len();
353 match dtype {
354 KernelDType::F32 => {
355 crate::fused::validate_plan_for_scalar::<f32>(plan, plan.input_count, output_count)
356 }
357 KernelDType::F64 => {
358 crate::fused::validate_plan_for_scalar::<f64>(plan, plan.input_count, output_count)
359 }
360 KernelDType::I32 => {
361 crate::fused::validate_plan_for_scalar::<i32>(plan, plan.input_count, output_count)
362 }
363 KernelDType::I64 => {
364 crate::fused::validate_plan_for_scalar::<i64>(plan, plan.input_count, output_count)
365 }
366 KernelDType::Bool => {
367 crate::fused::validate_plan_for_scalar::<bool>(plan, plan.input_count, output_count)
368 }
369 KernelDType::C32 => crate::fused::validate_plan_for_scalar::<Complex32>(
370 plan,
371 plan.input_count,
372 output_count,
373 ),
374 KernelDType::C64 => crate::fused::validate_plan_for_scalar::<Complex64>(
375 plan,
376 plan.input_count,
377 output_count,
378 ),
379 _ => Err(StridedError::UnsupportedDType {
380 dtype: dtype.label(),
381 }),
382 }
383}
384
385fn execute_fused_multi<T>(
386 plan: &FusedPlan,
387 dests: &mut [ErasedRawStridedMut<'_>],
388 inputs: &[ErasedRawStridedRef<'_>],
389 exec: Exec<'_>,
390) -> Result<()>
391where
392 T: FusedScalar + KernelStorageElement,
393{
394 if dests.len() == 1 {
398 let dims = dests[0].dims();
399 let strides = dests[0].strides();
400 let offset = dests[0].offset();
401 let data = dests[0].data_as_mut::<T>()?;
402 let mut dest_views =
403 [unsafe { StridedViewMut::new_unchecked(data, dims, strides, offset) }];
404 return execute_fused_inputs(&mut dest_views, inputs, plan, exec);
405 }
406
407 if dests.len() <= strided_basic::OPERAND_CAP {
408 let mut storage =
409 [const { MaybeUninit::<StridedViewMut<'_, T>>::uninit() }; strided_basic::OPERAND_CAP];
410 let mut views = unsafe { crate::fused::InitializedViewsMut::new(storage.as_mut_ptr()) };
413 for dest in dests.iter_mut() {
414 let dims = dest.dims();
415 let strides = dest.strides();
416 let offset = dest.offset();
417 let data = dest.data_as_mut::<T>()?;
418 views.push(unsafe { StridedViewMut::new_unchecked(data, dims, strides, offset) });
419 }
420 return execute_fused_inputs(views.as_mut_slice(), inputs, plan, exec);
421 }
422
423 let mut dest_views = Vec::with_capacity(dests.len());
424 for dest in dests.iter_mut() {
425 let dims = dest.dims();
426 let strides = dest.strides();
427 let offset = dest.offset();
428 let data = dest.data_as_mut::<T>()?;
429 dest_views.push(unsafe { StridedViewMut::new_unchecked(data, dims, strides, offset) });
430 }
431 execute_fused_inputs(&mut dest_views, inputs, plan, exec)
432}
433
434fn execute_fused_inputs<T>(
435 dests: &mut [StridedViewMut<'_, T>],
436 inputs: &[ErasedRawStridedRef<'_>],
437 plan: &FusedPlan,
438 exec: Exec<'_>,
439) -> Result<()>
440where
441 T: FusedScalar + KernelStorageElement,
442{
443 match inputs {
444 [a] => {
445 let views = [erased_view::<T>(a)?];
446 execute_fused_views(dests, &views, plan, exec)
447 }
448 [a, b] => {
449 let views = [erased_view::<T>(a)?, erased_view::<T>(b)?];
450 execute_fused_views(dests, &views, plan, exec)
451 }
452 [a, b, c] => {
453 let views = [
454 erased_view::<T>(a)?,
455 erased_view::<T>(b)?,
456 erased_view::<T>(c)?,
457 ];
458 execute_fused_views(dests, &views, plan, exec)
459 }
460 [a, b, c, d] => {
461 let views = [
462 erased_view::<T>(a)?,
463 erased_view::<T>(b)?,
464 erased_view::<T>(c)?,
465 erased_view::<T>(d)?,
466 ];
467 execute_fused_views(dests, &views, plan, exec)
468 }
469 _ => Err(StridedError::UnsupportedArity {
470 arity: inputs.len(),
471 max: ERASED_FUSED_INPUT_LIMIT,
472 }),
473 }
474}
475
476fn execute_fused_uninit_multi<T>(
477 plan: &FusedPlan,
478 dests: &mut [ErasedRawStridedUninitMut<'_>],
479 inputs: &[ErasedRawStridedPtr<'_>],
480 validated: &[ValidatedDestinationLayout],
481 exec: Exec<'_>,
482) -> Result<()>
483where
484 T: FusedScalar + KernelStorageElement,
485{
486 if dests.len() == 1 {
487 let dims = dests[0].dims();
488 let strides = dests[0].strides();
489 let offset = dests[0].offset();
490 let data = dests[0].data_as_uninit_mut::<T>()?;
491 let mut dest_views =
492 [unsafe { StridedViewMut::new_unchecked(data, dims, strides, offset) }];
493 return execute_fused_uninit_inputs(plan, &mut dest_views, inputs, validated, exec);
494 }
495
496 if dests.len() <= strided_basic::OPERAND_CAP {
497 let mut storage = [const { MaybeUninit::<StridedViewMut<'_, MaybeUninit<T>>>::uninit() };
498 strided_basic::OPERAND_CAP];
499 let mut views = unsafe { crate::fused::InitializedViewsMut::new(storage.as_mut_ptr()) };
502 for dest in dests.iter_mut() {
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 views.push(unsafe { StridedViewMut::new_unchecked(data, dims, strides, offset) });
508 }
509 return execute_fused_uninit_inputs(plan, views.as_mut_slice(), inputs, validated, exec);
510 }
511
512 let mut dest_views = Vec::with_capacity(dests.len());
513 for dest in dests.iter_mut() {
514 let dims = dest.dims();
515 let strides = dest.strides();
516 let offset = dest.offset();
517 let data = dest.data_as_uninit_mut::<T>()?;
518 dest_views.push(unsafe { StridedViewMut::new_unchecked(data, dims, strides, offset) });
519 }
520 execute_fused_uninit_inputs(plan, &mut dest_views, inputs, validated, exec)
521}
522
523fn execute_fused_uninit_inputs<T>(
524 plan: &FusedPlan,
525 dests: &mut [StridedViewMut<'_, MaybeUninit<T>>],
526 inputs: &[ErasedRawStridedPtr<'_>],
527 validated: &[ValidatedDestinationLayout],
528 exec: Exec<'_>,
529) -> Result<()>
530where
531 T: FusedScalar + KernelStorageElement,
532{
533 match inputs {
534 [a] => {
537 let refs = [unsafe { a.try_as_ref_after_no_overlap()? }];
538 let views = [erased_view::<T>(&refs[0])?];
539 crate::fused::fused_elementwise_into_uninit_multi(dests, &views, plan, validated, exec)
540 }
541 [a, b] => {
542 let refs = [
543 unsafe { a.try_as_ref_after_no_overlap()? },
545 unsafe { b.try_as_ref_after_no_overlap()? },
547 ];
548 let views = [erased_view::<T>(&refs[0])?, erased_view::<T>(&refs[1])?];
549 crate::fused::fused_elementwise_into_uninit_multi(dests, &views, plan, validated, exec)
550 }
551 [a, b, c] => {
552 let refs = [
553 unsafe { a.try_as_ref_after_no_overlap()? },
555 unsafe { b.try_as_ref_after_no_overlap()? },
557 unsafe { c.try_as_ref_after_no_overlap()? },
559 ];
560 let views = [
561 erased_view::<T>(&refs[0])?,
562 erased_view::<T>(&refs[1])?,
563 erased_view::<T>(&refs[2])?,
564 ];
565 crate::fused::fused_elementwise_into_uninit_multi(dests, &views, plan, validated, exec)
566 }
567 [a, b, c, d] => {
568 let refs = [
569 unsafe { a.try_as_ref_after_no_overlap()? },
571 unsafe { b.try_as_ref_after_no_overlap()? },
573 unsafe { c.try_as_ref_after_no_overlap()? },
575 unsafe { d.try_as_ref_after_no_overlap()? },
577 ];
578 let views = [
579 erased_view::<T>(&refs[0])?,
580 erased_view::<T>(&refs[1])?,
581 erased_view::<T>(&refs[2])?,
582 erased_view::<T>(&refs[3])?,
583 ];
584 crate::fused::fused_elementwise_into_uninit_multi(dests, &views, plan, validated, exec)
585 }
586 _ => Err(StridedError::UnsupportedArity {
587 arity: inputs.len(),
588 max: ERASED_FUSED_INPUT_LIMIT,
589 }),
590 }
591}
592
593fn execute_fused_views<T>(
594 dests: &mut [StridedViewMut<'_, T>],
595 inputs: &[StridedView<'_, T>],
596 plan: &FusedPlan,
597 exec: Exec<'_>,
598) -> Result<()>
599where
600 T: FusedScalar + KernelStorageElement,
601{
602 fused_elementwise_into(dests, inputs, plan, exec)
603}