use std::collections::{HashMap};
use super::{
code, NUM_REGISTERS,
Dataflow, Node, Op, LookupLeaf, Cold, Exit,
moves, all_registers,
};
use code::{Register, Slot, Variable, Action, EBB, Ending};
use crate::util::{ArrayMap};
#[derive(Debug)]
struct Block<L> {
actions: Box<[Action]>,
discriminant: Variable,
cold: Cold<EBB<L>>,
}
#[derive(Debug)]
pub struct CodeGen<'a, L: LookupLeaf> {
dataflow: &'a Dataflow,
lookup_leaf: &'a L,
allocation: HashMap<Node, Register>,
slots_used: usize,
registers: ArrayMap<Register, Option<Node>>,
variables: HashMap<Node, Variable>,
actions: Vec<Action>,
blocks: Vec<Block<L::Leaf>>,
}
impl<'a, L: LookupLeaf> CodeGen<'a, L> {
pub fn new(
dataflow: &'a Dataflow,
lookup_leaf: &'a L,
allocation: HashMap<Node, Register>,
slots_used: usize,
variables: HashMap<Node, Variable>,
) -> Self {
let mut registers = ArrayMap::new(NUM_REGISTERS);
for (&node, &v) in variables.iter() {
if let Variable::Register(r) = v {
registers[r] = Some(node);
}
}
Self {
dataflow,
lookup_leaf,
allocation,
slots_used,
registers,
variables,
actions: Vec::new(),
blocks: Vec::new(),
}
}
fn set_variable(&mut self, node: Node, new_v: impl Into<Variable>, old_v: Option<Variable>) {
let v = self.variables.insert(node, new_v.into());
assert_eq!(v, old_v);
}
pub fn slots_used(&self) -> usize {
self.slots_used
}
fn write(&mut self, node: Node) -> Register {
let r = self.allocation[&node];
self.set_variable(node, r, None);
self.registers[r] = Some(node);
r
}
fn spill(&mut self, node: Node) -> Register {
let r = self.allocation[&node];
assert_eq!(self.registers[r], Some(node));
let slot = Slot(self.slots_used);
self.slots_used += 1;
self.set_variable(node, slot, Some(r.into()));
r
}
pub fn read(&self, node: Node) -> Variable {
if let Some(&r) = self.allocation.get(&node) {
if self.registers[r] == Some(node) {
return r.into();
}
}
let v = self.variables[&node];
if let Variable::Register(_) = v {
panic!("Value is in a register that has been clobbered");
}
v
}
pub fn add_spill(&mut self, node1: Node, node2: Node) {
let r1 = self.spill(node1);
let r2 = self.spill(node2);
self.actions.push(Action::Push(Some(r1.into()), Some(r2.into())));
}
pub fn add_node(&mut self, n: Node) {
let df = self.dataflow;
let mut ins = Vec::with_capacity(3);
df.each_input(n, |in_, dep| {
if dep.is_value() { ins.push(self.read(in_)); }
});
let out = if df.has_out(n) { Some(self.write(n)) } else { None };
self.actions.push(Op::to_action(df.op(n), out, &ins));
}
pub fn add_guard(&mut self, guard: Node, cold: Cold<EBB<L::Leaf>>) {
let df = self.dataflow;
let discriminant = self.read(df.discriminant(guard));
let mut actions = Vec::new();
std::mem::swap(&mut actions, &mut self.actions);
self.blocks.push(Block {actions: actions.into(), discriminant, cold});
}
pub fn finish(mut self, exit: &Exit, leaf: L::Leaf) -> EBB<L::Leaf> {
let after = self.lookup_leaf.after(&leaf);
let mut dest_to_src: HashMap<Variable, Variable> =
exit.outputs.iter().zip(&*after.lives)
.map(|(&node, &dest)| (dest, self.read(node)))
.collect();
while self.slots_used < after.slots_used {
let src1 = dest_to_src.remove(&Slot(self.slots_used + 1).into());
let src2 = dest_to_src.remove(&Slot(self.slots_used).into());
self.actions.push(Action::Push(src1, src2));
self.slots_used += 2;
}
let mut uses: ArrayMap<Register, usize> = ArrayMap::new(NUM_REGISTERS);
for (&dest, &src) in &dest_to_src {
if let Variable::Register(r) = dest { uses[r] |= 1; }
if let Variable::Register(r) = src { uses[r] += 2; }
}
let temp = all_registers().min_by_key(|&r| uses[r]).unwrap();
let mut temp_replacement = Variable::from(Slot(self.slots_used));
if uses[temp] == 1 {
self.actions.push(Action::Push(None, None));
self.slots_used += 2;
} else if uses[temp] >= 2 {
self.actions.push(Action::Push(None, Some(temp.into())));
self.slots_used += 2;
} else {
temp_replacement = Variable::from(temp);
}
self.actions.extend(moves(dest_to_src, &temp.into()).map(
|(mut dest, mut src)| {
if src == temp.into() { src = temp_replacement; }
if dest == temp.into() { dest = temp_replacement; }
Action::Move(dest, src)
}
));
if uses[temp] & 1 != 0 {
self.actions.push(Action::Move(temp.into(), temp_replacement));
}
let num_drops = self.slots_used.checked_sub(after.slots_used).unwrap();
if num_drops > 0 {
assert_eq!(num_drops & 1, 0);
self.actions.push(Action::Drop(num_drops >> 1));
}
let mut ebb = EBB {actions: self.actions.into(), ending: Ending::Leaf(leaf)};
for Block {actions, discriminant, cold} in self.blocks.into_iter().rev() {
ebb = EBB {actions, ending: Ending::Switch(discriminant, cold.finish(ebb))};
};
ebb
}
}