use serde::{Deserialize, Serialize};
use vyre_foundation::ir::{ProgramGraph, ValueLifetime};
use crate::{ArtifactNodeId, ArtifactValueId};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum FusionRejectionReason {
UnknownGraphMember,
NotProducerConsumer,
LifecycleBoundary,
MultipleConsumers,
WorkgroupMismatch,
SynchronizationBoundary,
DependencyCycle,
}
impl FusionRejectionReason {
#[must_use]
pub const fn code(self) -> &'static str {
match self {
Self::UnknownGraphMember => "MKL001_UNKNOWN_GRAPH_MEMBER",
Self::NotProducerConsumer => "MKL002_NOT_PRODUCER_CONSUMER",
Self::LifecycleBoundary => "MKL003_LIFECYCLE_BOUNDARY",
Self::MultipleConsumers => "MKL004_MULTIPLE_CONSUMERS",
Self::WorkgroupMismatch => "MKL005_WORKGROUP_MISMATCH",
Self::SynchronizationBoundary => "MKL006_SYNCHRONIZATION_BOUNDARY",
Self::DependencyCycle => "MKL007_DEPENDENCY_CYCLE",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FusionDecision {
Legal,
Rejected(FusionRejectionReason),
}
#[must_use]
pub fn analyze_fusion_pair(
graph: &ProgramGraph,
from: ArtifactNodeId,
to: ArtifactNodeId,
value: ArtifactValueId,
) -> FusionDecision {
let Some(producer) = graph.nodes().get(from.0 as usize) else {
return FusionDecision::Rejected(FusionRejectionReason::UnknownGraphMember);
};
let Some(consumer) = graph.nodes().get(to.0 as usize) else {
return FusionDecision::Rejected(FusionRejectionReason::UnknownGraphMember);
};
let Some(value) = graph.values().get(value.0 as usize) else {
return FusionDecision::Rejected(FusionRejectionReason::UnknownGraphMember);
};
if value.producer.map(|id| id.0) != Some(from.0)
|| !value.consumers.iter().any(|id| id.0 == to.0)
{
return FusionDecision::Rejected(FusionRejectionReason::NotProducerConsumer);
}
if value.contract.lifetime != ValueLifetime::Invocation {
return FusionDecision::Rejected(FusionRejectionReason::LifecycleBoundary);
}
if value.consumers.len() != 1 {
return FusionDecision::Rejected(FusionRejectionReason::MultipleConsumers);
}
if producer.program.workgroup_size != consumer.program.workgroup_size {
return FusionDecision::Rejected(FusionRejectionReason::WorkgroupMismatch);
}
if producer.program.stats().has_node_barrier() || consumer.program.stats().has_node_barrier() {
return FusionDecision::Rejected(FusionRejectionReason::SynchronizationBoundary);
}
FusionDecision::Legal
}