use std::any::Any;
use std::fmt;
use std::hash::Hash;
use std::sync::Arc;
use crate::program::SemanticOperationView;
use crate::ExtensionCacheStore;
use tenferro_tensor::{Engine, Tensor, TensorRead};
use super::{
ExecutionContextIdentity, ExecutionContextMismatch, HardwareClassId, InputSignature,
PrepareError, PrepareOptionsKey, RegistrationIdentity, ResolvedPlanningConfig,
ResolvedProgramPlacement, RuntimeEpoch, RuntimeId, SpecializationProjection,
SpecializationRequirements,
};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum CoreCapabilityKind {
Elementwise,
Reduction,
Indexing,
DotGeneral,
Layout,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum UnsupportedReason {
MissingCapability {
capability: CoreCapabilityKind,
},
Operation {
operation: &'static str,
},
StorageClass {
storage_class: super::StorageClass,
},
}
impl fmt::Display for UnsupportedReason {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingCapability { capability } => {
write!(formatter, "missing {capability:?} capability")
}
Self::Operation { operation } => {
write!(formatter, "unsupported operation {operation:?}")
}
Self::StorageClass { storage_class } => {
write!(
formatter,
"unsupported storage class {:?}",
storage_class.as_str()
)
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct PreparationKeySummary {
compact_digest: u128,
}
impl PreparationKeySummary {
#[allow(dead_code)]
pub(crate) const fn new(compact_digest: u128) -> Self {
Self { compact_digest }
}
#[cfg(test)]
#[allow(dead_code)]
pub(crate) const fn for_test(value: u64) -> Self {
Self {
compact_digest: value as u128,
}
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct PreparedOperationBinding {
runtime_id: RuntimeId,
epoch: RuntimeEpoch,
engine_id: super::EngineId,
registration_identity: RegistrationIdentity,
context_identity: ExecutionContextIdentity,
hardware_class: HardwareClassId,
}
impl PreparedOperationBinding {
#[allow(dead_code)]
pub(crate) fn new(
runtime_id: RuntimeId,
epoch: RuntimeEpoch,
engine_id: super::EngineId,
registration_identity: RegistrationIdentity,
context_identity: ExecutionContextIdentity,
hardware_class: HardwareClassId,
) -> Self {
Self {
runtime_id,
epoch,
engine_id,
registration_identity,
context_identity,
hardware_class,
}
}
pub fn runtime_id(&self) -> RuntimeId {
self.runtime_id
}
pub fn epoch(&self) -> RuntimeEpoch {
self.epoch
}
pub fn engine_id(&self) -> &super::EngineId {
&self.engine_id
}
pub fn registration_identity(&self) -> RegistrationIdentity {
self.registration_identity
}
pub fn context_identity(&self) -> ExecutionContextIdentity {
self.context_identity
}
pub fn hardware_class(&self) -> &HardwareClassId {
&self.hardware_class
}
}
pub trait PreparedOperation: fmt::Debug + Send + Sync + 'static {
fn binding(&self) -> &PreparedOperationBinding;
fn specialization(&self) -> &SpecializationProjection;
fn retained_bytes(&self) -> usize;
}
#[doc(hidden)]
pub trait PreparedOperationExecutor: fmt::Debug + Send + Sync + 'static {
fn execute(
&self,
context: &mut ErasedExecutionContext<'_>,
extension_caches: &mut ExtensionCacheStore,
inputs: &[TensorRead<'_>],
) -> crate::Result<Vec<Tensor>>;
fn supports_session(&self) -> bool {
false
}
fn execute_in_session(
&self,
_session: &mut dyn Engine,
_extension_caches: &mut ExtensionCacheStore,
_inputs: &[TensorRead<'_>],
) -> crate::Result<Vec<Tensor>> {
Err(crate::Error::unsupported(
"prepared_operation.execute_in_session",
crate::ErrorPhase::Execution,
"prepared operation does not support the scheduler-owned backend session",
))
}
}
pub type PreparedOperationHandle = Arc<dyn PreparedOperation>;
#[doc(hidden)]
pub type PreparedOperationExecutorHandle = Arc<dyn PreparedOperationExecutor>;
#[derive(Clone)]
pub struct PreparedOperationPlan {
operation: PreparedOperationHandle,
executor: Option<PreparedOperationExecutorHandle>,
}
impl fmt::Debug for PreparedOperationPlan {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("PreparedOperationPlan")
.field("operation", &self.operation)
.field("has_executor", &self.executor.is_some())
.finish()
}
}
impl PreparedOperationPlan {
#[must_use]
pub fn metadata(operation: PreparedOperationHandle) -> Self {
Self {
operation,
executor: None,
}
}
#[must_use]
pub fn executable(
operation: PreparedOperationHandle,
executor: PreparedOperationExecutorHandle,
) -> Self {
Self {
operation,
executor: Some(executor),
}
}
#[must_use]
pub fn operation(&self) -> &PreparedOperationHandle {
&self.operation
}
#[doc(hidden)]
#[must_use]
pub fn executor(&self) -> Option<&PreparedOperationExecutorHandle> {
self.executor.as_ref()
}
#[must_use]
pub fn binding(&self) -> &PreparedOperationBinding {
self.operation.binding()
}
#[must_use]
pub fn specialization(&self) -> &SpecializationProjection {
self.operation.specialization()
}
#[must_use]
pub fn retained_bytes(&self) -> usize {
self.operation.retained_bytes()
}
}
#[derive(Clone, Debug)]
pub enum PrepareCapability {
Prepared(PreparedOperationPlan),
NeedsSpecialization(SpecializationRequirements),
Unsupported(UnsupportedReason),
}
pub struct ErasedExecutionContext<'a> {
identity: ExecutionContextIdentity,
value: &'a mut dyn Any,
}
impl<'a> ErasedExecutionContext<'a> {
pub fn new<T: 'static>(value: &'a mut T) -> Self {
Self {
identity: ExecutionContextIdentity::of::<T>(),
value,
}
}
pub fn from_dyn(identity: ExecutionContextIdentity, value: &'a mut dyn Any) -> Self {
Self { identity, value }
}
pub fn identity(&self) -> ExecutionContextIdentity {
self.identity
}
pub fn downcast_mut<T: 'static>(
&mut self,
expected: ExecutionContextIdentity,
) -> Result<&mut T, ExecutionContextMismatch> {
let requested = ExecutionContextIdentity::of::<T>();
if requested != expected {
return Err(ExecutionContextMismatch {
expected: requested,
actual: self.identity,
});
}
if self.identity != expected {
return Err(ExecutionContextMismatch {
expected,
actual: self.identity,
});
}
self.value
.downcast_mut::<T>()
.ok_or(ExecutionContextMismatch {
expected,
actual: self.identity,
})
}
}
impl fmt::Debug for ErasedExecutionContext<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ErasedExecutionContext")
.field("identity", &self.identity)
.finish_non_exhaustive()
}
}
pub struct CorePrepareContext<'a> {
binding: &'a PreparedOperationBinding,
inputs: &'a InputSignature,
resolved_placement: &'a ResolvedProgramPlacement,
planning: &'a ResolvedPlanningConfig,
prepare_options_key: &'a PrepareOptionsKey,
specialization: &'a SpecializationProjection,
}
impl<'a> CorePrepareContext<'a> {
#[allow(dead_code)]
pub(crate) fn new(
binding: &'a PreparedOperationBinding,
inputs: &'a InputSignature,
resolved_placement: &'a ResolvedProgramPlacement,
planning: &'a ResolvedPlanningConfig,
prepare_options_key: &'a PrepareOptionsKey,
specialization: &'a SpecializationProjection,
) -> Self {
Self {
binding,
inputs,
resolved_placement,
planning,
prepare_options_key,
specialization,
}
}
pub fn binding(&self) -> &'a PreparedOperationBinding {
self.binding
}
pub fn inputs(&self) -> &'a InputSignature {
self.inputs
}
pub fn resolved_placement(&self) -> &'a ResolvedProgramPlacement {
self.resolved_placement
}
pub fn planning(&self) -> &'a ResolvedPlanningConfig {
self.planning
}
pub fn prepare_options_key(&self) -> &'a PrepareOptionsKey {
self.prepare_options_key
}
pub fn specialization(&self) -> &'a SpecializationProjection {
self.specialization
}
}
impl fmt::Debug for CorePrepareContext<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("CorePrepareContext")
.field("binding", self.binding)
.field("inputs", &self.inputs.entries().len())
.field("resolved_placement", self.resolved_placement)
.field("planning", self.planning)
.field("prepare_options_key", self.prepare_options_key)
.field("specialization", self.specialization)
.finish()
}
}
macro_rules! request_type {
($name:ident) => {
pub struct $name<'a> {
operation: SemanticOperationView<'a>,
context: &'a CorePrepareContext<'a>,
}
impl<'a> $name<'a> {
#[allow(dead_code)]
pub(crate) fn new(
operation: SemanticOperationView<'a>,
context: &'a CorePrepareContext<'a>,
) -> Self {
Self { operation, context }
}
pub fn operation(&self) -> SemanticOperationView<'a> {
self.operation
}
pub fn context(&self) -> &'a CorePrepareContext<'a> {
self.context
}
}
impl fmt::Debug for $name<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct(stringify!($name))
.field("operation", &self.operation)
.field("context", &self.context)
.finish()
}
}
};
}
request_type!(ElementwisePrepareRequest);
request_type!(ReductionPrepareRequest);
request_type!(IndexingPrepareRequest);
request_type!(DotGeneralPrepareRequest);
request_type!(LayoutPrepareRequest);
pub trait ElementwiseRuntime: fmt::Debug + Send + Sync + 'static {
fn prepare(
&self,
request: ElementwisePrepareRequest<'_>,
) -> Result<PrepareCapability, PrepareError>;
fn max_fused_region_inputs(&self) -> Option<usize> {
None
}
}
pub trait ReductionRuntime: fmt::Debug + Send + Sync + 'static {
fn prepare(
&self,
request: ReductionPrepareRequest<'_>,
) -> Result<PrepareCapability, PrepareError>;
}
pub trait IndexingRuntime: fmt::Debug + Send + Sync + 'static {
fn prepare(
&self,
request: IndexingPrepareRequest<'_>,
) -> Result<PrepareCapability, PrepareError>;
}
pub trait DotGeneralPreparation: fmt::Debug + Send + Sync + 'static {
fn prepare(
&self,
request: DotGeneralPrepareRequest<'_>,
) -> Result<PrepareCapability, PrepareError>;
}
pub trait LayoutRuntime: fmt::Debug + Send + Sync + 'static {
fn prepare(&self, request: LayoutPrepareRequest<'_>)
-> Result<PrepareCapability, PrepareError>;
fn folds_metadata_views(&self) -> bool {
false
}
}
#[derive(Clone, Copy, Debug)]
enum ReservedSubgraphSlot {}
#[derive(Clone, Default)]
pub struct CoreCapabilityBundle {
elementwise: Option<Arc<dyn ElementwiseRuntime>>,
reduction: Option<Arc<dyn ReductionRuntime>>,
indexing: Option<Arc<dyn IndexingRuntime>>,
dot_general: Option<Arc<dyn DotGeneralPreparation>>,
layout: Option<Arc<dyn LayoutRuntime>>,
reserved_subgraph: Option<ReservedSubgraphSlot>,
}
impl CoreCapabilityBundle {
pub fn builder() -> CoreCapabilityBundleBuilder {
CoreCapabilityBundleBuilder::new()
}
pub fn elementwise(&self) -> Option<&Arc<dyn ElementwiseRuntime>> {
self.elementwise.as_ref()
}
pub fn reduction(&self) -> Option<&Arc<dyn ReductionRuntime>> {
self.reduction.as_ref()
}
pub fn indexing(&self) -> Option<&Arc<dyn IndexingRuntime>> {
self.indexing.as_ref()
}
pub fn dot_general(&self) -> Option<&Arc<dyn DotGeneralPreparation>> {
self.dot_general.as_ref()
}
pub fn layout(&self) -> Option<&Arc<dyn LayoutRuntime>> {
self.layout.as_ref()
}
}
impl fmt::Debug for CoreCapabilityBundle {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("CoreCapabilityBundle")
.field("elementwise", &self.elementwise.is_some())
.field("reduction", &self.reduction.is_some())
.field("indexing", &self.indexing.is_some())
.field("dot_general", &self.dot_general.is_some())
.field("layout", &self.layout.is_some())
.field("reserved_subgraph", &self.reserved_subgraph.is_some())
.finish()
}
}
#[derive(Clone, Default)]
pub struct CoreCapabilityBundleBuilder {
elementwise: Option<Arc<dyn ElementwiseRuntime>>,
reduction: Option<Arc<dyn ReductionRuntime>>,
indexing: Option<Arc<dyn IndexingRuntime>>,
dot_general: Option<Arc<dyn DotGeneralPreparation>>,
layout: Option<Arc<dyn LayoutRuntime>>,
}
impl CoreCapabilityBundleBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn elementwise(&mut self, capability: Arc<dyn ElementwiseRuntime>) -> &mut Self {
self.elementwise = Some(capability);
self
}
pub fn reduction(&mut self, capability: Arc<dyn ReductionRuntime>) -> &mut Self {
self.reduction = Some(capability);
self
}
pub fn indexing(&mut self, capability: Arc<dyn IndexingRuntime>) -> &mut Self {
self.indexing = Some(capability);
self
}
pub fn dot_general(&mut self, capability: Arc<dyn DotGeneralPreparation>) -> &mut Self {
self.dot_general = Some(capability);
self
}
pub fn layout(&mut self, capability: Arc<dyn LayoutRuntime>) -> &mut Self {
self.layout = Some(capability);
self
}
pub fn build(self) -> CoreCapabilityBundle {
CoreCapabilityBundle {
elementwise: self.elementwise,
reduction: self.reduction,
indexing: self.indexing,
dot_general: self.dot_general,
layout: self.layout,
reserved_subgraph: None,
}
}
}
impl fmt::Debug for CoreCapabilityBundleBuilder {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("CoreCapabilityBundleBuilder")
.field("elementwise", &self.elementwise.is_some())
.field("reduction", &self.reduction.is_some())
.field("indexing", &self.indexing.is_some())
.field("dot_general", &self.dot_general.is_some())
.field("layout", &self.layout.is_some())
.finish()
}
}