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
810/// Direct runtime provider for reduction core operations.
811pub trait ReductionRuntime: fmt::Debug + Send + Sync + 'static {
812 /// Prepare one reduction operation.
813 ///
814 /// # Errors
815 ///
816 /// Returns [`PrepareError`] when provider preparation fails.
817 fn prepare(
818 &self,
819 request: ReductionPrepareRequest<'_>,
820 ) -> Result<PrepareCapability, PrepareError>;
821}
822
823/// Direct runtime provider for indexing core operations.
824pub trait IndexingRuntime: fmt::Debug + Send + Sync + 'static {
825 /// Prepare one indexing operation.
826 ///
827 /// # Errors
828 ///
829 /// Returns [`PrepareError`] when provider preparation fails.
830 fn prepare(
831 &self,
832 request: IndexingPrepareRequest<'_>,
833 ) -> Result<PrepareCapability, PrepareError>;
834}
835
836/// Preparation-only direct runtime provider for dot-general operations.
837pub trait DotGeneralPreparation: fmt::Debug + Send + Sync + 'static {
838 /// Prepare one dot-general operation.
839 ///
840 /// # Errors
841 ///
842 /// Returns [`PrepareError`] when provider preparation fails.
843 fn prepare(
844 &self,
845 request: DotGeneralPrepareRequest<'_>,
846 ) -> Result<PrepareCapability, PrepareError>;
847}
848
849/// Direct runtime provider for layout core operations.
850pub trait LayoutRuntime: fmt::Debug + Send + Sync + 'static {
851 /// Prepare one layout operation.
852 ///
853 /// # Errors
854 ///
855 /// Returns [`PrepareError`] when provider preparation fails.
856 fn prepare(&self, request: LayoutPrepareRequest<'_>)
857 -> Result<PrepareCapability, PrepareError>;
858}
859
860#[derive(Clone, Copy, Debug)]
861enum ReservedSubgraphSlot {}
862
863/// Direct core capability slots captured by a runtime snapshot.
864///
865/// # Examples
866///
867/// ```
868/// use tenferro_runtime::CoreCapabilityBundle;
869///
870/// assert!(CoreCapabilityBundle::builder().build().elementwise().is_none());
871/// ```
872#[derive(Clone, Default)]
873pub struct CoreCapabilityBundle {
874 elementwise: Option<Arc<dyn ElementwiseRuntime>>,
875 reduction: Option<Arc<dyn ReductionRuntime>>,
876 indexing: Option<Arc<dyn IndexingRuntime>>,
877 dot_general: Option<Arc<dyn DotGeneralPreparation>>,
878 layout: Option<Arc<dyn LayoutRuntime>>,
879 reserved_subgraph: Option<ReservedSubgraphSlot>,
880}
881
882impl CoreCapabilityBundle {
883 /// Return an empty direct-capability builder.
884 pub fn builder() -> CoreCapabilityBundleBuilder {
885 CoreCapabilityBundleBuilder::new()
886 }
887
888 /// Return the elementwise provider slot.
889 pub fn elementwise(&self) -> Option<&Arc<dyn ElementwiseRuntime>> {
890 self.elementwise.as_ref()
891 }
892
893 /// Return the reduction provider slot.
894 pub fn reduction(&self) -> Option<&Arc<dyn ReductionRuntime>> {
895 self.reduction.as_ref()
896 }
897
898 /// Return the indexing provider slot.
899 pub fn indexing(&self) -> Option<&Arc<dyn IndexingRuntime>> {
900 self.indexing.as_ref()
901 }
902
903 /// Return the dot-general preparation slot.
904 pub fn dot_general(&self) -> Option<&Arc<dyn DotGeneralPreparation>> {
905 self.dot_general.as_ref()
906 }
907
908 /// Return the layout provider slot.
909 pub fn layout(&self) -> Option<&Arc<dyn LayoutRuntime>> {
910 self.layout.as_ref()
911 }
912}
913
914impl fmt::Debug for CoreCapabilityBundle {
915 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
916 formatter
917 .debug_struct("CoreCapabilityBundle")
918 .field("elementwise", &self.elementwise.is_some())
919 .field("reduction", &self.reduction.is_some())
920 .field("indexing", &self.indexing.is_some())
921 .field("dot_general", &self.dot_general.is_some())
922 .field("layout", &self.layout.is_some())
923 .field("reserved_subgraph", &self.reserved_subgraph.is_some())
924 .finish()
925 }
926}
927
928/// Builder for direct core capability slots.
929///
930/// Setters replace any previous value for the same slot and therefore are
931/// infallible.
932///
933/// # Examples
934///
935/// ```
936/// use tenferro_runtime::CoreCapabilityBundleBuilder;
937///
938/// assert!(CoreCapabilityBundleBuilder::new().build().layout().is_none());
939/// ```
940#[derive(Clone, Default)]
941pub struct CoreCapabilityBundleBuilder {
942 elementwise: Option<Arc<dyn ElementwiseRuntime>>,
943 reduction: Option<Arc<dyn ReductionRuntime>>,
944 indexing: Option<Arc<dyn IndexingRuntime>>,
945 dot_general: Option<Arc<dyn DotGeneralPreparation>>,
946 layout: Option<Arc<dyn LayoutRuntime>>,
947}
948
949impl CoreCapabilityBundleBuilder {
950 /// Return an empty builder.
951 pub fn new() -> Self {
952 Self::default()
953 }
954
955 /// Set or replace the elementwise slot.
956 pub fn elementwise(&mut self, capability: Arc<dyn ElementwiseRuntime>) -> &mut Self {
957 self.elementwise = Some(capability);
958 self
959 }
960
961 /// Set or replace the reduction slot.
962 pub fn reduction(&mut self, capability: Arc<dyn ReductionRuntime>) -> &mut Self {
963 self.reduction = Some(capability);
964 self
965 }
966
967 /// Set or replace the indexing slot.
968 pub fn indexing(&mut self, capability: Arc<dyn IndexingRuntime>) -> &mut Self {
969 self.indexing = Some(capability);
970 self
971 }
972
973 /// Set or replace the dot-general preparation slot.
974 pub fn dot_general(&mut self, capability: Arc<dyn DotGeneralPreparation>) -> &mut Self {
975 self.dot_general = Some(capability);
976 self
977 }
978
979 /// Set or replace the layout slot.
980 pub fn layout(&mut self, capability: Arc<dyn LayoutRuntime>) -> &mut Self {
981 self.layout = Some(capability);
982 self
983 }
984
985 /// Build an immutable capability bundle.
986 pub fn build(self) -> CoreCapabilityBundle {
987 CoreCapabilityBundle {
988 elementwise: self.elementwise,
989 reduction: self.reduction,
990 indexing: self.indexing,
991 dot_general: self.dot_general,
992 layout: self.layout,
993 reserved_subgraph: None,
994 }
995 }
996}
997
998impl fmt::Debug for CoreCapabilityBundleBuilder {
999 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1000 formatter
1001 .debug_struct("CoreCapabilityBundleBuilder")
1002 .field("elementwise", &self.elementwise.is_some())
1003 .field("reduction", &self.reduction.is_some())
1004 .field("indexing", &self.indexing.is_some())
1005 .field("dot_general", &self.dot_general.is_some())
1006 .field("layout", &self.layout.is_some())
1007 .finish()
1008 }
1009}