use crate::arm_independence::{
can_dispatch_concurrently, ArmBindingSummary, ArmIndependenceVerdict,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CrossPipelineFusionDecision {
Fuse,
KeepSeparate {
reason: CrossPipelineConflict,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CrossPipelineConflict {
WriteWriteConflict,
ReadAfterWrite,
WriteAfterRead,
}
impl std::fmt::Display for CrossPipelineConflict {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::WriteWriteConflict => f.write_str("write-write-conflict"),
Self::ReadAfterWrite => f.write_str("read-after-write"),
Self::WriteAfterRead => f.write_str("write-after-read"),
}
}
}
impl std::fmt::Display for CrossPipelineFusionDecision {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Fuse => f.write_str("fuse"),
Self::KeepSeparate { reason } => write!(f, "keep-separate:{reason}"),
}
}
}
#[must_use]
pub fn decide_cross_pipeline_fusion(
earlier: &ArmBindingSummary,
later: &ArmBindingSummary,
) -> CrossPipelineFusionDecision {
match can_dispatch_concurrently(earlier, later) {
ArmIndependenceVerdict::Independent => CrossPipelineFusionDecision::Fuse,
ArmIndependenceVerdict::SerializeRequired { reason } => {
CrossPipelineFusionDecision::KeepSeparate {
reason: match reason {
crate::arm_independence::ArmConflict::WriteWriteConflict => {
CrossPipelineConflict::WriteWriteConflict
}
crate::arm_independence::ArmConflict::ReadAfterWrite => {
CrossPipelineConflict::ReadAfterWrite
}
crate::arm_independence::ArmConflict::WriteAfterRead => {
CrossPipelineConflict::WriteAfterRead
}
},
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn summary(reads: &[u32], writes: &[u32]) -> ArmBindingSummary {
ArmBindingSummary {
reads: reads.iter().copied().collect(),
writes: writes.iter().copied().collect(),
}
}
#[test]
fn disjoint_pipelines_fuse() {
let a = summary(&[0, 1], &[2]);
let b = summary(&[3, 4], &[5]);
assert_eq!(
decide_cross_pipeline_fusion(&a, &b),
CrossPipelineFusionDecision::Fuse
);
}
#[test]
fn write_write_conflict_keeps_separate() {
let a = summary(&[0], &[2]);
let b = summary(&[1], &[2]);
assert_eq!(
decide_cross_pipeline_fusion(&a, &b),
CrossPipelineFusionDecision::KeepSeparate {
reason: CrossPipelineConflict::WriteWriteConflict,
}
);
}
#[test]
fn read_after_write_keeps_separate() {
let a = summary(&[0], &[2]);
let b = summary(&[2], &[3]);
assert_eq!(
decide_cross_pipeline_fusion(&a, &b),
CrossPipelineFusionDecision::KeepSeparate {
reason: CrossPipelineConflict::ReadAfterWrite,
}
);
}
#[test]
fn read_only_share_same_slot_fuses() {
let a = summary(&[0, 1], &[2]);
let b = summary(&[0, 1], &[3]);
assert_eq!(
decide_cross_pipeline_fusion(&a, &b),
CrossPipelineFusionDecision::Fuse
);
}
#[test]
fn cross_pipeline_decision_formats_human_string() {
assert_eq!(format!("{}", CrossPipelineConflict::WriteAfterRead), "write-after-read");
assert_eq!(
format!(
"{}",
CrossPipelineFusionDecision::KeepSeparate {
reason: CrossPipelineConflict::ReadAfterWrite
}
),
"keep-separate:read-after-write"
);
assert_eq!(format!("{}", CrossPipelineFusionDecision::Fuse), "fuse");
}
}