tenferro_runtime/runtime/capability.rs
1use std::any::Any;
2use std::fmt;
3use std::hash::Hash;
4use std::sync::Arc;
5
6use crate::program::SemanticOperationView;
7use crate::ExtensionCacheStore;
8use tenferro_tensor::{BackendSession, Tensor, TensorRead};
9
10use super::{
11 ExecutionContextIdentity, ExecutionContextMismatch, HardwareClassId, InputSignature,
12 PrepareError, PrepareOptionsKey, RegistrationIdentity, ResolvedPlanningConfig,
13 ResolvedProgramPlacement, RuntimeEpoch, RuntimeId, SpecializationProjection,
14 SpecializationRequirements,
15};
16
17/// Core operation family handled by a direct runtime capability slot.
18///
19/// # Examples
20///
21/// ```
22/// use tenferro_runtime::CoreCapabilityKind;
23///
24/// assert_eq!(CoreCapabilityKind::Elementwise, CoreCapabilityKind::Elementwise);
25/// ```
26#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
27pub enum CoreCapabilityKind {
28 /// Elementwise core operations.
29 Elementwise,
30 /// Reduction core operations.
31 Reduction,
32 /// Indexing and slicing core operations.
33 Indexing,
34 /// Dot-general preparation.
35 DotGeneral,
36 /// Layout-only operations.
37 Layout,
38}
39
40/// Deterministic reason why a provider cannot prepare an operation.
41///
42/// # Examples
43///
44/// ```
45/// use tenferro_runtime::{CoreCapabilityKind, UnsupportedReason};
46///
47/// let reason = UnsupportedReason::MissingCapability {
48/// capability: CoreCapabilityKind::Elementwise,
49/// };
50/// assert!(reason.to_string().contains("Elementwise"));
51/// ```
52#[derive(Clone, Debug, Eq, Hash, PartialEq)]
53#[non_exhaustive]
54pub enum UnsupportedReason {
55 /// The runtime snapshot has no provider for this core capability.
56 MissingCapability {
57 /// Missing core capability kind.
58 capability: CoreCapabilityKind,
59 },
60 /// The named operation is unsupported by the selected provider.
61 Operation {
62 /// Stable operation diagnostic.
63 operation: &'static str,
64 },
65 /// The selected provider does not support the required storage class.
66 StorageClass {
67 /// Unsupported storage class.
68 storage_class: super::StorageClass,
69 },
70}
71
72impl fmt::Display for UnsupportedReason {
73 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
74 match self {
75 Self::MissingCapability { capability } => {
76 write!(formatter, "missing {capability:?} capability")
77 }
78 Self::Operation { operation } => {
79 write!(formatter, "unsupported operation {operation:?}")
80 }
81 Self::StorageClass { storage_class } => {
82 write!(
83 formatter,
84 "unsupported storage class {:?}",
85 storage_class.as_str()
86 )
87 }
88 }
89 }
90}
91
92/// Bounded diagnostic summary of a preparation cache key.
93///
94/// A1 exposes only the summary shape. C1 owns the concrete key construction.
95///
96/// # Examples
97///
98/// ```
99/// use std::fmt::Debug;
100/// use tenferro_runtime::PreparationKeySummary;
101///
102/// fn requires_debug<T: Debug>() {}
103/// requires_debug::<PreparationKeySummary>();
104/// ```
105#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
106pub struct PreparationKeySummary {
107 compact_digest: u128,
108}
109
110impl PreparationKeySummary {
111 #[allow(dead_code)]
112 pub(crate) const fn new(compact_digest: u128) -> Self {
113 Self { compact_digest }
114 }
115
116 #[cfg(test)]
117 #[allow(dead_code)]
118 pub(crate) const fn for_test(value: u64) -> Self {
119 Self {
120 compact_digest: value as u128,
121 }
122 }
123}
124
125/// Runtime-created immutable binding for one prepared operation.
126///
127/// A1 has no public constructor. B0/C1 provide the runtime creation path.
128///
129/// # Examples
130///
131/// ```
132/// use std::fmt::Debug;
133/// use tenferro_runtime::PreparedOperationBinding;
134///
135/// fn requires_debug<T: Debug>() {}
136/// requires_debug::<PreparedOperationBinding>();
137/// ```
138#[derive(Clone, Debug, Eq, Hash, PartialEq)]
139pub struct PreparedOperationBinding {
140 runtime_id: RuntimeId,
141 epoch: RuntimeEpoch,
142 engine_id: super::EngineId,
143 registration_identity: RegistrationIdentity,
144 context_identity: ExecutionContextIdentity,
145 hardware_class: HardwareClassId,
146}
147
148impl PreparedOperationBinding {
149 #[allow(dead_code)]
150 pub(crate) fn new(
151 runtime_id: RuntimeId,
152 epoch: RuntimeEpoch,
153 engine_id: super::EngineId,
154 registration_identity: RegistrationIdentity,
155 context_identity: ExecutionContextIdentity,
156 hardware_class: HardwareClassId,
157 ) -> Self {
158 Self {
159 runtime_id,
160 epoch,
161 engine_id,
162 registration_identity,
163 context_identity,
164 hardware_class,
165 }
166 }
167
168 /// Return the runtime identity that created this binding.
169 ///
170 /// # Examples
171 ///
172 /// ```
173 /// use std::fmt::Debug;
174 /// use tenferro_runtime::PreparedOperationBinding;
175 ///
176 /// fn requires_debug<T: Debug>() {}
177 /// requires_debug::<PreparedOperationBinding>();
178 /// ```
179 pub fn runtime_id(&self) -> RuntimeId {
180 self.runtime_id
181 }
182
183 /// Return the immutable runtime epoch that created this binding.
184 ///
185 /// # Examples
186 ///
187 /// ```
188 /// use std::fmt::Debug;
189 /// use tenferro_runtime::PreparedOperationBinding;
190 ///
191 /// fn requires_debug<T: Debug>() {}
192 /// requires_debug::<PreparedOperationBinding>();
193 /// ```
194 pub fn epoch(&self) -> RuntimeEpoch {
195 self.epoch
196 }
197
198 /// Return the selected engine identifier.
199 ///
200 /// # Examples
201 ///
202 /// ```
203 /// use std::fmt::Debug;
204 /// use tenferro_runtime::PreparedOperationBinding;
205 ///
206 /// fn requires_debug<T: Debug>() {}
207 /// requires_debug::<PreparedOperationBinding>();
208 /// ```
209 pub fn engine_id(&self) -> &super::EngineId {
210 &self.engine_id
211 }
212
213 /// Return the runtime-local engine registration identity.
214 ///
215 /// # Examples
216 ///
217 /// ```
218 /// use std::fmt::Debug;
219 /// use tenferro_runtime::PreparedOperationBinding;
220 ///
221 /// fn requires_debug<T: Debug>() {}
222 /// requires_debug::<PreparedOperationBinding>();
223 /// ```
224 pub fn registration_identity(&self) -> RegistrationIdentity {
225 self.registration_identity
226 }
227
228 /// Return the execution-context type identity.
229 ///
230 /// # Examples
231 ///
232 /// ```
233 /// use std::fmt::Debug;
234 /// use tenferro_runtime::PreparedOperationBinding;
235 ///
236 /// fn requires_debug<T: Debug>() {}
237 /// requires_debug::<PreparedOperationBinding>();
238 /// ```
239 pub fn context_identity(&self) -> ExecutionContextIdentity {
240 self.context_identity
241 }
242
243 /// Return the selected hardware-class identifier.
244 ///
245 /// # Examples
246 ///
247 /// ```
248 /// use std::fmt::Debug;
249 /// use tenferro_runtime::PreparedOperationBinding;
250 ///
251 /// fn requires_debug<T: Debug>() {}
252 /// requires_debug::<PreparedOperationBinding>();
253 /// ```
254 pub fn hardware_class(&self) -> &HardwareClassId {
255 &self.hardware_class
256 }
257}
258
259/// Immutable metadata for a prepared provider operation.
260///
261/// # Examples
262///
263/// ```
264/// use tenferro_runtime::PreparedOperation;
265///
266/// fn accepts_prepared_operation(_: Option<&dyn PreparedOperation>) {}
267/// let _ = accepts_prepared_operation;
268/// ```
269pub trait PreparedOperation: fmt::Debug + Send + Sync + 'static {
270 /// Return the runtime-created binding carried by this operation.
271 fn binding(&self) -> &PreparedOperationBinding;
272 /// Return the specialization projection this operation depends on.
273 fn specialization(&self) -> &SpecializationProjection;
274 /// Return logical heap bytes owned exclusively by this operation.
275 fn retained_bytes(&self) -> usize;
276}
277
278#[doc(hidden)]
279/// Internal execution bridge for a prepared provider operation.
280///
281/// This remains a public trait only so out-of-crate extension providers can
282/// hand an executable plan back to the runtime. Public prepared-operation
283/// metadata stays on [`PreparedOperation`]; scheduled runtime execution owns
284/// when and how this bridge is invoked.
285pub trait PreparedOperationExecutor: fmt::Debug + Send + Sync + 'static {
286 /// Execute this prepared operation using a runtime-owned erased backend
287 /// context and extension cache store.
288 ///
289 /// # Errors
290 ///
291 /// Returns [`Error`] when the prepared operation is not executable for the
292 /// supplied context, input reads are invalid, or backend execution fails.
293 fn execute(
294 &self,
295 context: &mut ErasedExecutionContext<'_>,
296 extension_caches: &mut ExtensionCacheStore,
297 inputs: &[TensorRead<'_>],
298 ) -> crate::Result<Vec<Tensor>>;
299
300 /// Return whether this prepared operation can execute inside a borrowed
301 /// backend session.
302 ///
303 /// The scheduler uses this capability to form a session-compatible
304 /// admission region. A false result keeps the operation on the ordinary
305 /// backend-owned execution path; it is never silently retried from inside
306 /// a session.
307 fn supports_session(&self) -> bool {
308 false
309 }
310
311 /// Execute this prepared operation inside the scheduler-owned session.
312 ///
313 /// Implementations must use `session` directly and must not reacquire a
314 /// backend session. The borrowed session and any operation-local state
315 /// must not escape this call.
316 ///
317 /// # Errors
318 ///
319 /// The default implementation returns `Error::Unsupported` because the
320 /// prepared operation has no session-aware execution implementation.
321 /// Implementations may return the same typed error when the selected
322 /// backend session does not provide the required capability, or any other
323 /// typed execution error from the operation itself.
324 fn execute_in_session(
325 &self,
326 _session: &mut dyn BackendSession,
327 _extension_caches: &mut ExtensionCacheStore,
328 _inputs: &[TensorRead<'_>],
329 ) -> crate::Result<Vec<Tensor>> {
330 Err(crate::Error::unsupported(
331 "prepared_operation.execute_in_session",
332 crate::ErrorPhase::Execution,
333 "prepared operation does not support the scheduler-owned backend session",
334 ))
335 }
336}
337
338/// Shared prepared-operation handle.
339///
340/// # Examples
341///
342/// ```
343/// use std::fmt::Debug;
344/// use tenferro_runtime::PreparedOperationHandle;
345///
346/// fn requires_debug<T: Debug>() {}
347/// requires_debug::<PreparedOperationHandle>();
348/// ```
349pub type PreparedOperationHandle = Arc<dyn PreparedOperation>;
350
351#[doc(hidden)]
352/// Shared prepared-operation executor handle.
353///
354/// This alias is part of the runtime/provider bridge, not the user-facing
355/// prepared-operation metadata surface.
356pub type PreparedOperationExecutorHandle = Arc<dyn PreparedOperationExecutor>;
357
358/// Runtime-owned prepared operation plan with separated metadata and optional
359/// execution bridge.
360///
361/// Core operations are metadata-only because scheduled execution dispatches
362/// them through the selected engine's core executor. Extension operations carry
363/// an executor bridge that is invoked only by the scheduled runtime loop.
364///
365/// # Examples
366///
367/// ```
368/// use std::fmt::Debug;
369/// use tenferro_runtime::PreparedOperationPlan;
370///
371/// fn requires_debug<T: Debug>() {}
372/// requires_debug::<PreparedOperationPlan>();
373/// ```
374#[derive(Clone)]
375pub struct PreparedOperationPlan {
376 operation: PreparedOperationHandle,
377 executor: Option<PreparedOperationExecutorHandle>,
378}
379
380impl fmt::Debug for PreparedOperationPlan {
381 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
382 formatter
383 .debug_struct("PreparedOperationPlan")
384 .field("operation", &self.operation)
385 .field("has_executor", &self.executor.is_some())
386 .finish()
387 }
388}
389
390impl PreparedOperationPlan {
391 /// Construct a metadata-only prepared operation plan.
392 ///
393 /// # Examples
394 ///
395 /// ```
396 /// use tenferro_runtime::{PreparedOperationHandle, PreparedOperationPlan};
397 ///
398 /// fn provider_handle() -> PreparedOperationHandle {
399 /// unreachable!("example is type-checked but not executed")
400 /// }
401 ///
402 /// if false {
403 /// let plan = PreparedOperationPlan::metadata(provider_handle());
404 /// assert!(plan.executor().is_none());
405 /// }
406 /// ```
407 #[must_use]
408 pub fn metadata(operation: PreparedOperationHandle) -> Self {
409 Self {
410 operation,
411 executor: None,
412 }
413 }
414
415 /// Construct an executable prepared operation plan.
416 ///
417 /// # Examples
418 ///
419 /// ```
420 /// use tenferro_runtime::{
421 /// PreparedOperationExecutorHandle, PreparedOperationHandle, PreparedOperationPlan,
422 /// };
423 ///
424 /// fn provider_handle() -> PreparedOperationHandle {
425 /// unreachable!("example is type-checked but not executed")
426 /// }
427 ///
428 /// fn executor_handle() -> PreparedOperationExecutorHandle {
429 /// unreachable!("example is type-checked but not executed")
430 /// }
431 ///
432 /// if false {
433 /// let plan = PreparedOperationPlan::executable(provider_handle(), executor_handle());
434 /// assert!(plan.executor().is_some());
435 /// }
436 /// ```
437 #[must_use]
438 pub fn executable(
439 operation: PreparedOperationHandle,
440 executor: PreparedOperationExecutorHandle,
441 ) -> Self {
442 Self {
443 operation,
444 executor: Some(executor),
445 }
446 }
447
448 /// Return the prepared-operation metadata handle.
449 ///
450 /// # Examples
451 ///
452 /// ```
453 /// use tenferro_runtime::{PreparedOperationHandle, PreparedOperationPlan};
454 ///
455 /// fn provider_handle() -> PreparedOperationHandle {
456 /// unreachable!("example is type-checked but not executed")
457 /// }
458 ///
459 /// if false {
460 /// let plan = PreparedOperationPlan::metadata(provider_handle());
461 /// let _operation = plan.operation();
462 /// }
463 /// ```
464 #[must_use]
465 pub fn operation(&self) -> &PreparedOperationHandle {
466 &self.operation
467 }
468
469 /// Return the optional prepared-operation executor bridge.
470 #[doc(hidden)]
471 #[must_use]
472 pub fn executor(&self) -> Option<&PreparedOperationExecutorHandle> {
473 self.executor.as_ref()
474 }
475
476 /// Return the runtime-created binding carried by the metadata operation.
477 ///
478 /// # Examples
479 ///
480 /// ```
481 /// use tenferro_runtime::{PreparedOperationHandle, PreparedOperationPlan};
482 ///
483 /// fn provider_handle() -> PreparedOperationHandle {
484 /// unreachable!("example is type-checked but not executed")
485 /// }
486 ///
487 /// if false {
488 /// let plan = PreparedOperationPlan::metadata(provider_handle());
489 /// let _binding = plan.binding();
490 /// }
491 /// ```
492 #[must_use]
493 pub fn binding(&self) -> &PreparedOperationBinding {
494 self.operation.binding()
495 }
496
497 /// Return the specialization projection carried by the metadata operation.
498 ///
499 /// # Examples
500 ///
501 /// ```
502 /// use tenferro_runtime::{PreparedOperationHandle, PreparedOperationPlan};
503 ///
504 /// fn provider_handle() -> PreparedOperationHandle {
505 /// unreachable!("example is type-checked but not executed")
506 /// }
507 ///
508 /// if false {
509 /// let plan = PreparedOperationPlan::metadata(provider_handle());
510 /// let _specialization = plan.specialization();
511 /// }
512 /// ```
513 #[must_use]
514 pub fn specialization(&self) -> &SpecializationProjection {
515 self.operation.specialization()
516 }
517
518 /// Return logical heap bytes owned by metadata and executor handles.
519 ///
520 /// # Examples
521 ///
522 /// ```
523 /// use tenferro_runtime::{PreparedOperationHandle, PreparedOperationPlan};
524 ///
525 /// fn provider_handle() -> PreparedOperationHandle {
526 /// unreachable!("example is type-checked but not executed")
527 /// }
528 ///
529 /// if false {
530 /// let plan = PreparedOperationPlan::metadata(provider_handle());
531 /// let _bytes = plan.retained_bytes();
532 /// }
533 /// ```
534 #[must_use]
535 pub fn retained_bytes(&self) -> usize {
536 self.operation.retained_bytes()
537 }
538}
539
540/// Result of asking a provider to prepare an operation.
541///
542/// # Examples
543///
544/// ```
545/// use tenferro_runtime::{CoreCapabilityKind, PrepareCapability, UnsupportedReason};
546///
547/// let capability = PrepareCapability::Unsupported(UnsupportedReason::MissingCapability {
548/// capability: CoreCapabilityKind::Elementwise,
549/// });
550/// assert!(matches!(capability, PrepareCapability::Unsupported { .. }));
551/// ```
552#[derive(Clone, Debug)]
553pub enum PrepareCapability {
554 /// Provider returned an immutable prepared operation.
555 Prepared(PreparedOperationPlan),
556 /// Provider requires a wider specialization before it can prepare.
557 NeedsSpecialization(SpecializationRequirements),
558 /// Provider does not support this request.
559 Unsupported(UnsupportedReason),
560}
561
562/// Type-erased execution context with a checked identity.
563///
564/// # Examples
565///
566/// ```
567/// use tenferro_runtime::{ErasedExecutionContext, ExecutionContextIdentity};
568///
569/// let mut value = 1_u64;
570/// let erased = ErasedExecutionContext::new(&mut value);
571/// assert_eq!(erased.identity(), ExecutionContextIdentity::of::<u64>());
572/// ```
573pub struct ErasedExecutionContext<'a> {
574 identity: ExecutionContextIdentity,
575 value: &'a mut dyn Any,
576}
577
578impl<'a> ErasedExecutionContext<'a> {
579 /// Erase a concrete execution context while retaining its type identity.
580 ///
581 /// # Examples
582 ///
583 /// ```
584 /// use tenferro_runtime::ErasedExecutionContext;
585 ///
586 /// let mut value = 1_u64;
587 /// let erased = ErasedExecutionContext::new(&mut value);
588 /// assert_eq!(erased.identity().type_name(), std::any::type_name::<u64>());
589 /// ```
590 pub fn new<T: 'static>(value: &'a mut T) -> Self {
591 Self {
592 identity: ExecutionContextIdentity::of::<T>(),
593 value,
594 }
595 }
596
597 /// Return the stored execution-context type identity.
598 ///
599 /// # Examples
600 ///
601 /// ```
602 /// use tenferro_runtime::{ErasedExecutionContext, ExecutionContextIdentity};
603 ///
604 /// let mut value = 1_u64;
605 /// let erased = ErasedExecutionContext::new(&mut value);
606 /// assert_eq!(erased.identity(), ExecutionContextIdentity::of::<u64>());
607 /// ```
608 pub fn identity(&self) -> ExecutionContextIdentity {
609 self.identity
610 }
611
612 /// Downcast to the requested execution-context type after checking identity.
613 ///
614 /// # Examples
615 ///
616 /// ```
617 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
618 /// use tenferro_runtime::{ErasedExecutionContext, ExecutionContextIdentity};
619 ///
620 /// let mut value = 1_u64;
621 /// let mut erased = ErasedExecutionContext::new(&mut value);
622 /// *erased.downcast_mut::<u64>(ExecutionContextIdentity::of::<u64>())? = 2;
623 /// # Ok(())
624 /// # }
625 /// ```
626 ///
627 /// # Errors
628 ///
629 /// Returns [`ExecutionContextMismatch`] when the stored identity, supplied
630 /// expected identity, and requested type do not all agree.
631 pub fn downcast_mut<T: 'static>(
632 &mut self,
633 expected: ExecutionContextIdentity,
634 ) -> Result<&mut T, ExecutionContextMismatch> {
635 let requested = ExecutionContextIdentity::of::<T>();
636 if requested != expected {
637 return Err(ExecutionContextMismatch {
638 expected: requested,
639 actual: self.identity,
640 });
641 }
642 if self.identity != expected {
643 return Err(ExecutionContextMismatch {
644 expected,
645 actual: self.identity,
646 });
647 }
648 self.value
649 .downcast_mut::<T>()
650 .ok_or(ExecutionContextMismatch {
651 expected,
652 actual: self.identity,
653 })
654 }
655}
656
657impl fmt::Debug for ErasedExecutionContext<'_> {
658 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
659 formatter
660 .debug_struct("ErasedExecutionContext")
661 .field("identity", &self.identity)
662 .finish_non_exhaustive()
663 }
664}
665
666/// Runtime-created borrowed context passed to direct core providers.
667///
668/// # Examples
669///
670/// ```
671/// use std::fmt::Debug;
672/// use tenferro_runtime::CorePrepareContext;
673///
674/// fn requires_debug<T: Debug>() {}
675/// requires_debug::<CorePrepareContext<'_>>();
676/// ```
677pub struct CorePrepareContext<'a> {
678 binding: &'a PreparedOperationBinding,
679 inputs: &'a InputSignature,
680 resolved_placement: &'a ResolvedProgramPlacement,
681 planning: &'a ResolvedPlanningConfig,
682 prepare_options_key: &'a PrepareOptionsKey,
683 specialization: &'a SpecializationProjection,
684}
685
686impl<'a> CorePrepareContext<'a> {
687 #[allow(dead_code)]
688 pub(crate) fn new(
689 binding: &'a PreparedOperationBinding,
690 inputs: &'a InputSignature,
691 resolved_placement: &'a ResolvedProgramPlacement,
692 planning: &'a ResolvedPlanningConfig,
693 prepare_options_key: &'a PrepareOptionsKey,
694 specialization: &'a SpecializationProjection,
695 ) -> Self {
696 Self {
697 binding,
698 inputs,
699 resolved_placement,
700 planning,
701 prepare_options_key,
702 specialization,
703 }
704 }
705
706 /// Return the runtime-created binding.
707 pub fn binding(&self) -> &'a PreparedOperationBinding {
708 self.binding
709 }
710
711 /// Return value-free input metadata.
712 pub fn inputs(&self) -> &'a InputSignature {
713 self.inputs
714 }
715
716 /// Return the selected program placement.
717 pub fn resolved_placement(&self) -> &'a ResolvedProgramPlacement {
718 self.resolved_placement
719 }
720
721 /// Return resolved planning policy.
722 pub fn planning(&self) -> &'a ResolvedPlanningConfig {
723 self.planning
724 }
725
726 /// Return the normalized prepare-options key.
727 pub fn prepare_options_key(&self) -> &'a PrepareOptionsKey {
728 self.prepare_options_key
729 }
730
731 /// Return the concrete specialization projection.
732 pub fn specialization(&self) -> &'a SpecializationProjection {
733 self.specialization
734 }
735}
736
737impl fmt::Debug for CorePrepareContext<'_> {
738 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
739 formatter
740 .debug_struct("CorePrepareContext")
741 .field("binding", self.binding)
742 .field("inputs", &self.inputs.entries().len())
743 .field("resolved_placement", self.resolved_placement)
744 .field("planning", self.planning)
745 .field("prepare_options_key", self.prepare_options_key)
746 .field("specialization", self.specialization)
747 .finish()
748 }
749}
750
751macro_rules! request_type {
752 ($name:ident) => {
753 /// Runtime-created direct core preparation request.
754 pub struct $name<'a> {
755 operation: SemanticOperationView<'a>,
756 context: &'a CorePrepareContext<'a>,
757 }
758
759 impl<'a> $name<'a> {
760 #[allow(dead_code)]
761 pub(crate) fn new(
762 operation: SemanticOperationView<'a>,
763 context: &'a CorePrepareContext<'a>,
764 ) -> Self {
765 Self { operation, context }
766 }
767
768 /// Return the immutable semantic operation view.
769 pub fn operation(&self) -> SemanticOperationView<'a> {
770 self.operation
771 }
772
773 /// Return the shared runtime preparation context.
774 pub fn context(&self) -> &'a CorePrepareContext<'a> {
775 self.context
776 }
777 }
778
779 impl fmt::Debug for $name<'_> {
780 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
781 formatter
782 .debug_struct(stringify!($name))
783 .field("operation", &self.operation)
784 .field("context", &self.context)
785 .finish()
786 }
787 }
788 };
789}
790
791request_type!(ElementwisePrepareRequest);
792request_type!(ReductionPrepareRequest);
793request_type!(IndexingPrepareRequest);
794request_type!(DotGeneralPrepareRequest);
795request_type!(LayoutPrepareRequest);
796
797/// Direct runtime provider for elementwise core operations.
798pub trait ElementwiseRuntime: fmt::Debug + Send + Sync + 'static {
799 /// Prepare one elementwise operation.
800 ///
801 /// # Errors
802 ///
803 /// Returns [`PrepareError`] when provider preparation fails.
804 fn prepare(
805 &self,
806 request: ElementwisePrepareRequest<'_>,
807 ) -> Result<PrepareCapability, PrepareError>;
808
809 /// Largest number of distinct external inputs one fused elementwise region
810 /// may read on this engine, or `None` when the engine has no such limit.
811 ///
812 /// The prepared planner does not plan a region above this limit, so the
813 /// region runs instruction by instruction without a fusion attempt that the
814 /// backend would decline anyway. The default is `None`.
815 ///
816 /// # Examples
817 ///
818 /// ```rust
819 /// use tenferro_cpu::CpuBackend;
820 /// use tenferro_runtime::ElementwiseRuntime;
821 ///
822 /// // The CPU engine's fused kernel reads at most four region inputs.
823 /// let cpu = CpuBackend::new();
824 /// assert_eq!(cpu.max_fused_region_inputs(), Some(4));
825 /// ```
826 fn max_fused_region_inputs(&self) -> Option<usize> {
827 None
828 }
829}
830
831/// Direct runtime provider for reduction core operations.
832pub trait ReductionRuntime: fmt::Debug + Send + Sync + 'static {
833 /// Prepare one reduction operation.
834 ///
835 /// # Errors
836 ///
837 /// Returns [`PrepareError`] when provider preparation fails.
838 fn prepare(
839 &self,
840 request: ReductionPrepareRequest<'_>,
841 ) -> Result<PrepareCapability, PrepareError>;
842}
843
844/// Direct runtime provider for indexing core operations.
845pub trait IndexingRuntime: fmt::Debug + Send + Sync + 'static {
846 /// Prepare one indexing operation.
847 ///
848 /// # Errors
849 ///
850 /// Returns [`PrepareError`] when provider preparation fails.
851 fn prepare(
852 &self,
853 request: IndexingPrepareRequest<'_>,
854 ) -> Result<PrepareCapability, PrepareError>;
855}
856
857/// Preparation-only direct runtime provider for dot-general operations.
858pub trait DotGeneralPreparation: fmt::Debug + Send + Sync + 'static {
859 /// Prepare one dot-general operation.
860 ///
861 /// # Errors
862 ///
863 /// Returns [`PrepareError`] when provider preparation fails.
864 fn prepare(
865 &self,
866 request: DotGeneralPrepareRequest<'_>,
867 ) -> Result<PrepareCapability, PrepareError>;
868}
869
870/// Direct runtime provider for layout core operations.
871pub trait LayoutRuntime: fmt::Debug + Send + Sync + 'static {
872 /// Prepare one layout operation.
873 ///
874 /// # Errors
875 ///
876 /// Returns [`PrepareError`] when provider preparation fails.
877 fn prepare(&self, request: LayoutPrepareRequest<'_>)
878 -> Result<PrepareCapability, PrepareError>;
879}
880
881#[derive(Clone, Copy, Debug)]
882enum ReservedSubgraphSlot {}
883
884/// Direct core capability slots captured by a runtime snapshot.
885///
886/// # Examples
887///
888/// ```
889/// use tenferro_runtime::CoreCapabilityBundle;
890///
891/// assert!(CoreCapabilityBundle::builder().build().elementwise().is_none());
892/// ```
893#[derive(Clone, Default)]
894pub struct CoreCapabilityBundle {
895 elementwise: Option<Arc<dyn ElementwiseRuntime>>,
896 reduction: Option<Arc<dyn ReductionRuntime>>,
897 indexing: Option<Arc<dyn IndexingRuntime>>,
898 dot_general: Option<Arc<dyn DotGeneralPreparation>>,
899 layout: Option<Arc<dyn LayoutRuntime>>,
900 reserved_subgraph: Option<ReservedSubgraphSlot>,
901}
902
903impl CoreCapabilityBundle {
904 /// Return an empty direct-capability builder.
905 pub fn builder() -> CoreCapabilityBundleBuilder {
906 CoreCapabilityBundleBuilder::new()
907 }
908
909 /// Return the elementwise provider slot.
910 pub fn elementwise(&self) -> Option<&Arc<dyn ElementwiseRuntime>> {
911 self.elementwise.as_ref()
912 }
913
914 /// Return the reduction provider slot.
915 pub fn reduction(&self) -> Option<&Arc<dyn ReductionRuntime>> {
916 self.reduction.as_ref()
917 }
918
919 /// Return the indexing provider slot.
920 pub fn indexing(&self) -> Option<&Arc<dyn IndexingRuntime>> {
921 self.indexing.as_ref()
922 }
923
924 /// Return the dot-general preparation slot.
925 pub fn dot_general(&self) -> Option<&Arc<dyn DotGeneralPreparation>> {
926 self.dot_general.as_ref()
927 }
928
929 /// Return the layout provider slot.
930 pub fn layout(&self) -> Option<&Arc<dyn LayoutRuntime>> {
931 self.layout.as_ref()
932 }
933}
934
935impl fmt::Debug for CoreCapabilityBundle {
936 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
937 formatter
938 .debug_struct("CoreCapabilityBundle")
939 .field("elementwise", &self.elementwise.is_some())
940 .field("reduction", &self.reduction.is_some())
941 .field("indexing", &self.indexing.is_some())
942 .field("dot_general", &self.dot_general.is_some())
943 .field("layout", &self.layout.is_some())
944 .field("reserved_subgraph", &self.reserved_subgraph.is_some())
945 .finish()
946 }
947}
948
949/// Builder for direct core capability slots.
950///
951/// Setters replace any previous value for the same slot and therefore are
952/// infallible.
953///
954/// # Examples
955///
956/// ```
957/// use tenferro_runtime::CoreCapabilityBundleBuilder;
958///
959/// assert!(CoreCapabilityBundleBuilder::new().build().layout().is_none());
960/// ```
961#[derive(Clone, Default)]
962pub struct CoreCapabilityBundleBuilder {
963 elementwise: Option<Arc<dyn ElementwiseRuntime>>,
964 reduction: Option<Arc<dyn ReductionRuntime>>,
965 indexing: Option<Arc<dyn IndexingRuntime>>,
966 dot_general: Option<Arc<dyn DotGeneralPreparation>>,
967 layout: Option<Arc<dyn LayoutRuntime>>,
968}
969
970impl CoreCapabilityBundleBuilder {
971 /// Return an empty builder.
972 pub fn new() -> Self {
973 Self::default()
974 }
975
976 /// Set or replace the elementwise slot.
977 pub fn elementwise(&mut self, capability: Arc<dyn ElementwiseRuntime>) -> &mut Self {
978 self.elementwise = Some(capability);
979 self
980 }
981
982 /// Set or replace the reduction slot.
983 pub fn reduction(&mut self, capability: Arc<dyn ReductionRuntime>) -> &mut Self {
984 self.reduction = Some(capability);
985 self
986 }
987
988 /// Set or replace the indexing slot.
989 pub fn indexing(&mut self, capability: Arc<dyn IndexingRuntime>) -> &mut Self {
990 self.indexing = Some(capability);
991 self
992 }
993
994 /// Set or replace the dot-general preparation slot.
995 pub fn dot_general(&mut self, capability: Arc<dyn DotGeneralPreparation>) -> &mut Self {
996 self.dot_general = Some(capability);
997 self
998 }
999
1000 /// Set or replace the layout slot.
1001 pub fn layout(&mut self, capability: Arc<dyn LayoutRuntime>) -> &mut Self {
1002 self.layout = Some(capability);
1003 self
1004 }
1005
1006 /// Build an immutable capability bundle.
1007 pub fn build(self) -> CoreCapabilityBundle {
1008 CoreCapabilityBundle {
1009 elementwise: self.elementwise,
1010 reduction: self.reduction,
1011 indexing: self.indexing,
1012 dot_general: self.dot_general,
1013 layout: self.layout,
1014 reserved_subgraph: None,
1015 }
1016 }
1017}
1018
1019impl fmt::Debug for CoreCapabilityBundleBuilder {
1020 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1021 formatter
1022 .debug_struct("CoreCapabilityBundleBuilder")
1023 .field("elementwise", &self.elementwise.is_some())
1024 .field("reduction", &self.reduction.is_some())
1025 .field("indexing", &self.indexing.is_some())
1026 .field("dot_general", &self.dot_general.is_some())
1027 .field("layout", &self.layout.is_some())
1028 .finish()
1029 }
1030}