use std::collections::BTreeSet;
use crate::hir::common::{HirExpr, HirLValue, HirStmt, TempId};
use super::binding::{CarryBinding, TempBindingRewrite, carry_binding_from_lvalue};
use super::reads::BindingReadCollector;
pub(super) struct BindingHandoffSeed {
pub(super) rewrites: Vec<TempBindingRewrite>,
pub(super) retained_pairs: Vec<(HirLValue, HirExpr)>,
}
pub(super) fn binding_handoff_seed(stmt: &HirStmt) -> Option<BindingHandoffSeed> {
let HirStmt::Assign(assign) = stmt else {
return None;
};
if assign.targets.len() < 2 || assign.targets.len() != assign.values.len() {
return None;
}
let mut seen_targets = BTreeSet::new();
let mut seen_bindings = BTreeSet::new();
let mut rewrites = Vec::with_capacity(assign.targets.len());
let mut retained_pairs = Vec::new();
for (target, value) in assign.targets.iter().zip(&assign.values) {
let rewrite = match (target, value) {
(HirLValue::Temp(target_temp), HirExpr::LocalRef(local)) => Some(TempBindingRewrite {
from: *target_temp,
to: CarryBinding::Local(*local),
}),
(HirLValue::Temp(target_temp), HirExpr::TempRef(temp)) => Some(TempBindingRewrite {
from: *target_temp,
to: CarryBinding::Temp(*temp),
}),
_ => None,
};
let Some(rewrite) = rewrite else {
retained_pairs.push((target.clone(), value.clone()));
continue;
};
if !seen_targets.insert(rewrite.from) || !seen_bindings.insert(rewrite.to) {
return None;
}
rewrites.push(rewrite);
}
if rewrites.is_empty() {
return None;
}
Some(BindingHandoffSeed {
rewrites,
retained_pairs,
})
}
pub(super) fn rewrite_binding_handoff_seed(
stmt: &mut HirStmt,
retained_pairs: &[(HirLValue, HirExpr)],
) -> bool {
let HirStmt::Assign(assign) = stmt else {
return false;
};
assign.targets = retained_pairs
.iter()
.map(|(target, _)| target.clone())
.collect();
assign.values = retained_pairs
.iter()
.map(|(_, value)| value.clone())
.collect();
true
}
pub(super) fn direct_temp_writeback_stmt(stmt: &HirStmt) -> Option<(CarryBinding, TempId)> {
let HirStmt::Assign(assign) = stmt else {
return None;
};
let [target] = assign.targets.as_slice() else {
return None;
};
let [HirExpr::TempRef(update_temp)] = assign.values.as_slice() else {
return None;
};
let carried = carry_binding_from_lvalue(target)?;
if matches!(carried, CarryBinding::Temp(temp) if temp == *update_temp) {
return None;
}
Some((carried, *update_temp))
}
pub(super) fn update_handoff_seed(stmt: &HirStmt) -> Option<(TempId, CarryBinding)> {
let HirStmt::Assign(assign) = stmt else {
return None;
};
let [HirLValue::Temp(target_temp)] = assign.targets.as_slice() else {
return None;
};
let [value] = assign.values.as_slice() else {
return None;
};
if matches!(value, HirExpr::LocalRef(_) | HirExpr::TempRef(_)) {
return None;
}
let mut collector = BindingReadCollector::default();
collector.collect_expr(value);
let carried = collector.single_read()?;
match carried {
CarryBinding::Temp(temp) if temp == *target_temp => None,
_ => Some((*target_temp, carried)),
}
}
pub(super) fn rewrite_update_handoff_seed(stmt: &mut HirStmt, carried: CarryBinding) -> bool {
let HirStmt::Assign(assign) = stmt else {
return false;
};
let [target] = assign.targets.as_mut_slice() else {
return false;
};
*target = match carried {
CarryBinding::Local(local) => HirLValue::Local(local),
CarryBinding::Temp(temp) => HirLValue::Temp(temp),
};
true
}
pub(super) fn local_handoff_seed(stmt: &HirStmt) -> Option<(TempId, crate::hir::common::LocalId)> {
let HirStmt::Assign(assign) = stmt else {
return None;
};
let [HirLValue::Temp(temp)] = assign.targets.as_slice() else {
return None;
};
let [HirExpr::LocalRef(local)] = assign.values.as_slice() else {
return None;
};
Some((*temp, *local))
}
pub(super) fn single_binding_handoff_seed(stmt: &HirStmt) -> Option<(TempId, CarryBinding)> {
let HirStmt::Assign(assign) = stmt else {
return None;
};
let [HirLValue::Temp(temp)] = assign.targets.as_slice() else {
return None;
};
let [value] = assign.values.as_slice() else {
return None;
};
let binding = match value {
HirExpr::LocalRef(local) => CarryBinding::Local(*local),
HirExpr::TempRef(source) => CarryBinding::Temp(*source),
_ => return None,
};
Some((*temp, binding))
}