use super::ir::*;
pub(super) fn split_output_only(prog: &mut TapeProgram) {
let sec = prog.section_range(Cadence::Continuous);
prog.n_rhs = sec.len() as u32;
let tables = prog.tables();
let mut units: Vec<std::ops::Range<usize>> = Vec::new();
let mut pc = sec.start;
while pc < sec.end {
let len = match &prog.instrs[pc] {
Instr::JmpIfZero {
n_true, n_false, ..
} => 1 + (*n_true + *n_false) as usize,
Instr::Sweep { spec } => 1 + prog.sweeps[*spec as usize].body_len as usize,
_ => 1,
};
let end = (pc + len).min(sec.end);
units.push(pc..end);
pc = end;
}
let mut def_unit: Vec<u32> = vec![u32::MAX; prog.slots.len()];
for (u, r) in units.iter().enumerate() {
for ins in &prog.instrs[r.clone()] {
ins.for_each_def(&tables, |s| def_unit[s as usize] = u as u32);
}
}
let root = |ins: &Instr| {
if let Instr::Lanes { spec } = ins {
return tables.lanes[*spec as usize]
.writes
.iter()
.any(|w| matches!(w.dst, LaneDst::Dy(_)));
}
matches!(
ins,
Instr::DyWrite { .. }
| Instr::Fault { .. }
| Instr::FaultScan { .. }
| Instr::ScalarRead { .. }
| Instr::DataGather { faults: true, .. }
| Instr::Interp { .. }
| Instr::LoadForcing { .. }
| Instr::Sweep { .. }
)
};
let mut needed = vec![false; units.len()];
for u in (0..units.len()).rev() {
if !needed[u] && !prog.instrs[units[u].clone()].iter().any(root) {
continue;
}
needed[u] = true;
for ins in &prog.instrs[units[u].clone()] {
ins.for_each_read(&tables, |s| {
let d = def_unit[s as usize];
if d != u32::MAX && (d as usize) < u {
needed[d as usize] = true;
}
});
}
}
let n_needed: usize = units
.iter()
.zip(&needed)
.filter(|(_, n)| **n)
.map(|(r, _)| r.len())
.sum();
if n_needed == sec.len() {
return;
}
let order: Vec<usize> = units
.iter()
.zip(&needed)
.filter(|(_, n)| **n)
.chain(units.iter().zip(&needed).filter(|(_, n)| !**n))
.flat_map(|(r, _)| r.clone())
.collect();
let tagged = !prog.precision.is_empty();
let instrs: Vec<Instr> = order.iter().map(|&i| prog.instrs[i].clone()).collect();
let prov: Vec<u32> = order.iter().map(|&i| prog.provenance[i]).collect();
let prec: Vec<_> = if tagged {
order.iter().map(|&i| prog.precision[i]).collect()
} else {
Vec::new()
};
prog.instrs.splice(sec.clone(), instrs);
prog.provenance.splice(sec.clone(), prov);
if tagged {
prog.precision.splice(sec, prec);
}
prog.n_rhs = n_needed as u32;
}