use std::collections::{HashSet};
use super::{GLOBAL, Variable, IntoVariable, Action, Switch};
#[derive(Debug, Clone)]
pub struct Convention {
pub lives: Box<[Variable]>,
pub slots_used: usize,
}
impl Convention {
pub fn refines(&self, old: &Self) -> bool {
let old_lives: HashSet<Variable> = old.lives.iter().copied().collect();
self.lives.iter().all(|v| old_lives.contains(v)) && self.slots_used == old.slots_used
}
}
impl Default for Convention {
fn default() -> Self {
Convention {lives: Box::new([GLOBAL.into()]), slots_used: 0}
}
}
pub struct Propagator {
lives: HashSet<Variable>,
slots_used: usize,
}
impl Propagator {
pub fn new(after: &Convention) -> Self {
Self {
lives: after.lives.iter().copied().collect(),
slots_used: after.slots_used,
}
}
pub fn insert(&mut self, src: impl IntoVariable) {
self.lives.insert(src.into());
}
pub fn remove(&mut self, dest: impl IntoVariable) {
self.lives.remove(&dest.into());
}
pub fn branch(&mut self, other: &Convention) {
for &v in other.lives.iter() {
self.insert(v);
}
assert_eq!(self.slots_used, other.slots_used);
}
pub fn switch<'a, C>(
discriminant: Variable,
switch: &'a Switch<C>,
to_convention: impl Fn(&C)-> &'a Convention,
) -> Self {
let mut ret: Option<Self> = None;
switch.map(|c| {
let convention = to_convention(c);
if let Some(ref mut ret) = ret {
ret.branch(convention);
} else {
ret = Some(Self::new(convention));
}
});
let mut ret = ret.expect("Switch has no cases");
ret.insert(discriminant);
ret
}
pub fn action(&mut self, action: Action) {
use Action::*;
match action {
Move(dest, src) => {
self.remove(dest);
self.insert(src);
},
Constant(_, dest, _) => {
self.remove(dest);
},
Unary(_, _, dest, src) => {
self.remove(dest);
self.insert(src);
},
Binary(_, _, dest, src1, src2) => {
self.remove(dest);
self.insert(src1);
self.insert(src2);
},
Load(dest, addr) => {
self.remove(dest);
self.insert(addr.base);
},
Store(dest, src, addr) => {
self.remove(dest);
self.insert(src);
self.insert(addr.base);
},
Send(dest, src1, src2) => {
self.remove(dest);
self.insert(src1);
self.insert(src2);
},
Push(src1, src2) => {
if let Some(src) = src1 {
self.insert(src);
}
if let Some(src) = src2 {
self.insert(src);
}
self.slots_used -= 2;
},
Drop(n) => {
self.slots_used += 2 * n;
},
Debug(src) => {
self.insert(src);
},
}
}
pub fn before(&self) -> Convention {
Convention {
lives: self.lives.iter().copied().collect(),
slots_used: self.slots_used,
}
}
}