Skip to main content

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::{Engine, 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 Engine,
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    /// Wrap an engine already erased to `dyn Any`, with the identity of its
609    /// concrete type.
610    ///
611    /// # Examples
612    ///
613    /// ```
614    /// use tenferro_runtime::{ErasedExecutionContext, ExecutionContextIdentity};
615    ///
616    /// let mut value = 7_u32;
617    /// let context = ErasedExecutionContext::from_dyn(ExecutionContextIdentity::of::<u32>(), &mut value);
618    /// assert_eq!(context.identity(), ExecutionContextIdentity::of::<u32>());
619    /// ```
620    pub fn from_dyn(identity: ExecutionContextIdentity, value: &'a mut dyn Any) -> Self {
621        Self { identity, value }
622    }
623
624    pub fn identity(&self) -> ExecutionContextIdentity {
625        self.identity
626    }
627
628    /// Downcast to the requested execution-context type after checking identity.
629    ///
630    /// # Examples
631    ///
632    /// ```
633    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
634    /// use tenferro_runtime::{ErasedExecutionContext, ExecutionContextIdentity};
635    ///
636    /// let mut value = 1_u64;
637    /// let mut erased = ErasedExecutionContext::new(&mut value);
638    /// *erased.downcast_mut::<u64>(ExecutionContextIdentity::of::<u64>())? = 2;
639    /// # Ok(())
640    /// # }
641    /// ```
642    ///
643    /// # Errors
644    ///
645    /// Returns [`ExecutionContextMismatch`] when the stored identity, supplied
646    /// expected identity, and requested type do not all agree.
647    pub fn downcast_mut<T: 'static>(
648        &mut self,
649        expected: ExecutionContextIdentity,
650    ) -> Result<&mut T, ExecutionContextMismatch> {
651        let requested = ExecutionContextIdentity::of::<T>();
652        if requested != expected {
653            return Err(ExecutionContextMismatch {
654                expected: requested,
655                actual: self.identity,
656            });
657        }
658        if self.identity != expected {
659            return Err(ExecutionContextMismatch {
660                expected,
661                actual: self.identity,
662            });
663        }
664        self.value
665            .downcast_mut::<T>()
666            .ok_or(ExecutionContextMismatch {
667                expected,
668                actual: self.identity,
669            })
670    }
671}
672
673impl fmt::Debug for ErasedExecutionContext<'_> {
674    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
675        formatter
676            .debug_struct("ErasedExecutionContext")
677            .field("identity", &self.identity)
678            .finish_non_exhaustive()
679    }
680}
681
682/// Runtime-created borrowed context passed to direct core providers.
683///
684/// # Examples
685///
686/// ```
687/// use std::fmt::Debug;
688/// use tenferro_runtime::CorePrepareContext;
689///
690/// fn requires_debug<T: Debug>() {}
691/// requires_debug::<CorePrepareContext<'_>>();
692/// ```
693pub struct CorePrepareContext<'a> {
694    binding: &'a PreparedOperationBinding,
695    inputs: &'a InputSignature,
696    resolved_placement: &'a ResolvedProgramPlacement,
697    planning: &'a ResolvedPlanningConfig,
698    prepare_options_key: &'a PrepareOptionsKey,
699    specialization: &'a SpecializationProjection,
700}
701
702impl<'a> CorePrepareContext<'a> {
703    #[allow(dead_code)]
704    pub(crate) fn new(
705        binding: &'a PreparedOperationBinding,
706        inputs: &'a InputSignature,
707        resolved_placement: &'a ResolvedProgramPlacement,
708        planning: &'a ResolvedPlanningConfig,
709        prepare_options_key: &'a PrepareOptionsKey,
710        specialization: &'a SpecializationProjection,
711    ) -> Self {
712        Self {
713            binding,
714            inputs,
715            resolved_placement,
716            planning,
717            prepare_options_key,
718            specialization,
719        }
720    }
721
722    /// Return the runtime-created binding.
723    pub fn binding(&self) -> &'a PreparedOperationBinding {
724        self.binding
725    }
726
727    /// Return value-free input metadata.
728    pub fn inputs(&self) -> &'a InputSignature {
729        self.inputs
730    }
731
732    /// Return the selected program placement.
733    pub fn resolved_placement(&self) -> &'a ResolvedProgramPlacement {
734        self.resolved_placement
735    }
736
737    /// Return resolved planning policy.
738    pub fn planning(&self) -> &'a ResolvedPlanningConfig {
739        self.planning
740    }
741
742    /// Return the normalized prepare-options key.
743    pub fn prepare_options_key(&self) -> &'a PrepareOptionsKey {
744        self.prepare_options_key
745    }
746
747    /// Return the concrete specialization projection.
748    pub fn specialization(&self) -> &'a SpecializationProjection {
749        self.specialization
750    }
751}
752
753impl fmt::Debug for CorePrepareContext<'_> {
754    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
755        formatter
756            .debug_struct("CorePrepareContext")
757            .field("binding", self.binding)
758            .field("inputs", &self.inputs.entries().len())
759            .field("resolved_placement", self.resolved_placement)
760            .field("planning", self.planning)
761            .field("prepare_options_key", self.prepare_options_key)
762            .field("specialization", self.specialization)
763            .finish()
764    }
765}
766
767macro_rules! request_type {
768    ($name:ident) => {
769        /// Runtime-created direct core preparation request.
770        pub struct $name<'a> {
771            operation: SemanticOperationView<'a>,
772            context: &'a CorePrepareContext<'a>,
773        }
774
775        impl<'a> $name<'a> {
776            #[allow(dead_code)]
777            pub(crate) fn new(
778                operation: SemanticOperationView<'a>,
779                context: &'a CorePrepareContext<'a>,
780            ) -> Self {
781                Self { operation, context }
782            }
783
784            /// Return the immutable semantic operation view.
785            pub fn operation(&self) -> SemanticOperationView<'a> {
786                self.operation
787            }
788
789            /// Return the shared runtime preparation context.
790            pub fn context(&self) -> &'a CorePrepareContext<'a> {
791                self.context
792            }
793        }
794
795        impl fmt::Debug for $name<'_> {
796            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
797                formatter
798                    .debug_struct(stringify!($name))
799                    .field("operation", &self.operation)
800                    .field("context", &self.context)
801                    .finish()
802            }
803        }
804    };
805}
806
807request_type!(ElementwisePrepareRequest);
808request_type!(ReductionPrepareRequest);
809request_type!(IndexingPrepareRequest);
810request_type!(DotGeneralPrepareRequest);
811request_type!(LayoutPrepareRequest);
812
813/// Direct runtime provider for elementwise core operations.
814pub trait ElementwiseRuntime: fmt::Debug + Send + Sync + 'static {
815    /// Prepare one elementwise operation.
816    ///
817    /// # Errors
818    ///
819    /// Returns [`PrepareError`] when provider preparation fails.
820    fn prepare(
821        &self,
822        request: ElementwisePrepareRequest<'_>,
823    ) -> Result<PrepareCapability, PrepareError>;
824
825    /// Largest number of distinct external inputs one fused elementwise region
826    /// may read on this engine, or `None` when the engine has no such limit.
827    ///
828    /// The prepared planner does not plan a region above this limit, so the
829    /// region runs instruction by instruction without a fusion attempt that the
830    /// backend would decline anyway. The default is `None`.
831    ///
832    /// # Examples
833    ///
834    /// ```rust
835    /// use tenferro_cpu::CpuEngine;
836    ///
837    /// // The CPU engine registration exposes its own fused-region limit.
838    /// let registration = tenferro_cpu::runtime_engine_registration(&CpuEngine::serial())?;
839    /// let elementwise = registration.capabilities().elementwise().unwrap();
840    /// assert_eq!(elementwise.max_fused_region_inputs(), Some(4));
841    /// # Ok::<(), Box<dyn std::error::Error>>(())
842    /// ```
843    fn max_fused_region_inputs(&self) -> Option<usize> {
844        None
845    }
846}
847
848/// Direct runtime provider for reduction core operations.
849pub trait ReductionRuntime: fmt::Debug + Send + Sync + 'static {
850    /// Prepare one reduction operation.
851    ///
852    /// # Errors
853    ///
854    /// Returns [`PrepareError`] when provider preparation fails.
855    fn prepare(
856        &self,
857        request: ReductionPrepareRequest<'_>,
858    ) -> Result<PrepareCapability, PrepareError>;
859}
860
861/// Direct runtime provider for indexing core operations.
862pub trait IndexingRuntime: fmt::Debug + Send + Sync + 'static {
863    /// Prepare one indexing operation.
864    ///
865    /// # Errors
866    ///
867    /// Returns [`PrepareError`] when provider preparation fails.
868    fn prepare(
869        &self,
870        request: IndexingPrepareRequest<'_>,
871    ) -> Result<PrepareCapability, PrepareError>;
872}
873
874/// Preparation-only direct runtime provider for dot-general operations.
875pub trait DotGeneralPreparation: fmt::Debug + Send + Sync + 'static {
876    /// Prepare one dot-general operation.
877    ///
878    /// # Errors
879    ///
880    /// Returns [`PrepareError`] when provider preparation fails.
881    fn prepare(
882        &self,
883        request: DotGeneralPrepareRequest<'_>,
884    ) -> Result<PrepareCapability, PrepareError>;
885}
886
887/// Direct runtime provider for layout core operations.
888pub trait LayoutRuntime: fmt::Debug + Send + Sync + 'static {
889    /// Prepare one layout operation.
890    ///
891    /// # Errors
892    ///
893    /// Returns [`PrepareError`] when provider preparation fails.
894    fn prepare(&self, request: LayoutPrepareRequest<'_>)
895        -> Result<PrepareCapability, PrepareError>;
896
897    /// Whether this engine's `*_read` entries consume a strided view in place,
898    /// so a metadata-only operation (transpose, reshape, broadcast, slice)
899    /// with one reading consumer may become that consumer's input view at
900    /// preparation instead of running as its own command (#2226).
901    ///
902    /// The default is `false`: an engine whose read entries reject or stage a
903    /// view keeps every metadata operation as a materializing command, and its
904    /// schedule is unchanged.
905    ///
906    /// # Examples
907    ///
908    /// ```rust
909    /// use tenferro_cpu::CpuEngine;
910    ///
911    /// // The CPU engine reads views in place for every operation family.
912    /// let registration = tenferro_cpu::runtime_engine_registration(&CpuEngine::serial())?;
913    /// let layout = registration.capabilities().layout().unwrap();
914    /// assert!(layout.folds_metadata_views());
915    /// # Ok::<(), Box<dyn std::error::Error>>(())
916    /// ```
917    fn folds_metadata_views(&self) -> bool {
918        false
919    }
920}
921
922#[derive(Clone, Copy, Debug)]
923enum ReservedSubgraphSlot {}
924
925/// Direct core capability slots captured by a runtime snapshot.
926///
927/// # Examples
928///
929/// ```
930/// use tenferro_runtime::CoreCapabilityBundle;
931///
932/// assert!(CoreCapabilityBundle::builder().build().elementwise().is_none());
933/// ```
934#[derive(Clone, Default)]
935pub struct CoreCapabilityBundle {
936    elementwise: Option<Arc<dyn ElementwiseRuntime>>,
937    reduction: Option<Arc<dyn ReductionRuntime>>,
938    indexing: Option<Arc<dyn IndexingRuntime>>,
939    dot_general: Option<Arc<dyn DotGeneralPreparation>>,
940    layout: Option<Arc<dyn LayoutRuntime>>,
941    reserved_subgraph: Option<ReservedSubgraphSlot>,
942}
943
944impl CoreCapabilityBundle {
945    /// Return an empty direct-capability builder.
946    pub fn builder() -> CoreCapabilityBundleBuilder {
947        CoreCapabilityBundleBuilder::new()
948    }
949
950    /// Return the elementwise provider slot.
951    pub fn elementwise(&self) -> Option<&Arc<dyn ElementwiseRuntime>> {
952        self.elementwise.as_ref()
953    }
954
955    /// Return the reduction provider slot.
956    pub fn reduction(&self) -> Option<&Arc<dyn ReductionRuntime>> {
957        self.reduction.as_ref()
958    }
959
960    /// Return the indexing provider slot.
961    pub fn indexing(&self) -> Option<&Arc<dyn IndexingRuntime>> {
962        self.indexing.as_ref()
963    }
964
965    /// Return the dot-general preparation slot.
966    pub fn dot_general(&self) -> Option<&Arc<dyn DotGeneralPreparation>> {
967        self.dot_general.as_ref()
968    }
969
970    /// Return the layout provider slot.
971    pub fn layout(&self) -> Option<&Arc<dyn LayoutRuntime>> {
972        self.layout.as_ref()
973    }
974}
975
976impl fmt::Debug for CoreCapabilityBundle {
977    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
978        formatter
979            .debug_struct("CoreCapabilityBundle")
980            .field("elementwise", &self.elementwise.is_some())
981            .field("reduction", &self.reduction.is_some())
982            .field("indexing", &self.indexing.is_some())
983            .field("dot_general", &self.dot_general.is_some())
984            .field("layout", &self.layout.is_some())
985            .field("reserved_subgraph", &self.reserved_subgraph.is_some())
986            .finish()
987    }
988}
989
990/// Builder for direct core capability slots.
991///
992/// Setters replace any previous value for the same slot and therefore are
993/// infallible.
994///
995/// # Examples
996///
997/// ```
998/// use tenferro_runtime::CoreCapabilityBundleBuilder;
999///
1000/// assert!(CoreCapabilityBundleBuilder::new().build().layout().is_none());
1001/// ```
1002#[derive(Clone, Default)]
1003pub struct CoreCapabilityBundleBuilder {
1004    elementwise: Option<Arc<dyn ElementwiseRuntime>>,
1005    reduction: Option<Arc<dyn ReductionRuntime>>,
1006    indexing: Option<Arc<dyn IndexingRuntime>>,
1007    dot_general: Option<Arc<dyn DotGeneralPreparation>>,
1008    layout: Option<Arc<dyn LayoutRuntime>>,
1009}
1010
1011impl CoreCapabilityBundleBuilder {
1012    /// Return an empty builder.
1013    pub fn new() -> Self {
1014        Self::default()
1015    }
1016
1017    /// Set or replace the elementwise slot.
1018    pub fn elementwise(&mut self, capability: Arc<dyn ElementwiseRuntime>) -> &mut Self {
1019        self.elementwise = Some(capability);
1020        self
1021    }
1022
1023    /// Set or replace the reduction slot.
1024    pub fn reduction(&mut self, capability: Arc<dyn ReductionRuntime>) -> &mut Self {
1025        self.reduction = Some(capability);
1026        self
1027    }
1028
1029    /// Set or replace the indexing slot.
1030    pub fn indexing(&mut self, capability: Arc<dyn IndexingRuntime>) -> &mut Self {
1031        self.indexing = Some(capability);
1032        self
1033    }
1034
1035    /// Set or replace the dot-general preparation slot.
1036    pub fn dot_general(&mut self, capability: Arc<dyn DotGeneralPreparation>) -> &mut Self {
1037        self.dot_general = Some(capability);
1038        self
1039    }
1040
1041    /// Set or replace the layout slot.
1042    pub fn layout(&mut self, capability: Arc<dyn LayoutRuntime>) -> &mut Self {
1043        self.layout = Some(capability);
1044        self
1045    }
1046
1047    /// Build an immutable capability bundle.
1048    pub fn build(self) -> CoreCapabilityBundle {
1049        CoreCapabilityBundle {
1050            elementwise: self.elementwise,
1051            reduction: self.reduction,
1052            indexing: self.indexing,
1053            dot_general: self.dot_general,
1054            layout: self.layout,
1055            reserved_subgraph: None,
1056        }
1057    }
1058}
1059
1060impl fmt::Debug for CoreCapabilityBundleBuilder {
1061    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1062        formatter
1063            .debug_struct("CoreCapabilityBundleBuilder")
1064            .field("elementwise", &self.elementwise.is_some())
1065            .field("reduction", &self.reduction.is_some())
1066            .field("indexing", &self.indexing.is_some())
1067            .field("dot_general", &self.dot_general.is_some())
1068            .field("layout", &self.layout.is_some())
1069            .finish()
1070    }
1071}