use crate::asm_generation::{
register_sequencer::RegisterSequencer, RegisterAllocationStatus, RegisterPool,
};
use crate::asm_lang::{virtual_register::*, RealizedOp, VirtualOp};
use petgraph::graph::NodeIndex;
use std::collections::{BTreeSet, HashMap};
pub type InterferenceGraph =
petgraph::stable_graph::StableGraph<VirtualRegister, (), petgraph::Undirected>;
pub(crate) fn liveness_analysis(ops: &[RealizedOp]) -> HashMap<usize, BTreeSet<VirtualRegister>> {
let mut live_in: HashMap<usize, BTreeSet<VirtualRegister>> =
HashMap::from_iter((0..ops.len()).into_iter().map(|idx| (idx, BTreeSet::new())));
let mut live_out: HashMap<usize, BTreeSet<VirtualRegister>> =
HashMap::from_iter((0..ops.len()).into_iter().map(|idx| (idx, BTreeSet::new())));
let offset_to_ix = HashMap::from_iter(ops.iter().enumerate().map(|(idx, op)| (op.offset, idx)));
let mut modified = true;
while modified {
modified = false;
for (ix, op) in ops.iter().rev().enumerate() {
let rev_ix = ops.len() - ix - 1;
let mut op_use = op.opcode.use_registers();
let mut op_def = op.opcode.def_registers();
op_use.retain(|®| matches!(reg, VirtualRegister::Virtual(_)));
op_def.retain(|®| matches!(reg, VirtualRegister::Virtual(_)));
let prev_live_out_op = live_out.get(&rev_ix).expect("ix must exist").clone();
let prev_live_in_op = live_in.get(&rev_ix).expect("ix must exist").clone();
let live_out_op = live_out.get_mut(&rev_ix).expect("ix must exist");
for s in &op.opcode.successors(rev_ix, ops, &offset_to_ix) {
for l in live_in.get(s).expect("ix must exist") {
live_out_op.insert(l.clone());
}
}
let live_in_op = live_in.get_mut(&rev_ix).expect("ix must exist");
for u in op_use {
live_in_op.insert(u.clone());
}
let mut live_out_op_minus_defs = live_out_op.clone();
for d in &op_def {
live_out_op_minus_defs.remove(d);
}
for l in &live_out_op_minus_defs {
live_in_op.insert(l.clone());
}
modified |= (prev_live_in_op != *live_in_op) || (prev_live_out_op != *live_out_op);
}
}
live_out
}
pub(crate) fn create_interference_graph(
ops: &[RealizedOp],
live_out: &HashMap<usize, BTreeSet<VirtualRegister>>,
) -> (InterferenceGraph, HashMap<VirtualRegister, NodeIndex>) {
let mut interference_graph = InterferenceGraph::with_capacity(0, 0);
let mut reg_to_node_map: HashMap<VirtualRegister, NodeIndex> = HashMap::new();
ops.iter()
.fold(BTreeSet::new(), |mut tree, elem| {
let mut regs = elem.opcode.registers();
regs.retain(|®| matches!(reg, VirtualRegister::Virtual(_)));
tree.extend(regs.into_iter());
tree
})
.iter()
.for_each(|®| {
reg_to_node_map.insert(reg.clone(), interference_graph.add_node(reg.clone()));
});
for (ix, regs) in live_out {
match &ops[*ix].opcode {
VirtualOp::MOVE(v, c) => {
if let Some(ix1) = reg_to_node_map.get(v) {
for b in regs.iter() {
if let Some(ix2) = reg_to_node_map.get(b) {
if *b != *c && *b != *v && !interference_graph.contains_edge(*ix1, *ix2)
{
interference_graph.add_edge(*ix1, *ix2, ());
}
}
}
}
}
_ => {
for v in &ops[*ix].opcode.def_registers() {
if let Some(ix1) = reg_to_node_map.get(v) {
for b in regs.iter() {
if let Some(ix2) = reg_to_node_map.get(b) {
if *b != **v && !interference_graph.contains_edge(*ix1, *ix2) {
interference_graph.add_edge(*ix1, *ix2, ());
}
}
}
}
}
}
}
}
(interference_graph, reg_to_node_map)
}
pub(crate) fn coalesce_registers(
ops: &[RealizedOp],
interference_graph: &mut InterferenceGraph,
reg_to_node_map: &mut HashMap<VirtualRegister, NodeIndex>,
register_sequencer: &mut RegisterSequencer,
) -> Vec<RealizedOp> {
let mut reg_to_reg_map: HashMap<VirtualRegister, VirtualRegister> = HashMap::new();
let mut reduced_ops: Vec<RealizedOp> = vec![];
let mut offset_map: HashMap<u64, u64> = HashMap::new();
let mut num_moves_removed = 0;
for op in ops {
let new_op = RealizedOp {
opcode: op.opcode.clone(),
owning_span: op.owning_span.clone(),
comment: op.comment.clone(),
offset: op.offset - num_moves_removed,
};
offset_map.insert(op.offset, op.offset - num_moves_removed);
match &op.opcode {
VirtualOp::MOVE(x, y) => {
match (x, y) {
(VirtualRegister::Virtual(_), VirtualRegister::Virtual(_)) => {
let regs = vec![x.clone(), y.clone()]
.iter()
.map(|reg| {
let mut temp = reg.clone();
while let Some(t) = reg_to_reg_map.get(&temp) {
temp = t.clone();
}
temp
})
.collect::<Vec<_>>();
let (r1, r2) = (®s[0], ®s[1]);
let ix1 = reg_to_node_map.get(r1).unwrap();
let ix2 = reg_to_node_map.get(r2).unwrap();
if r1 == r2 {
num_moves_removed += 1;
continue;
}
if interference_graph.contains_edge(*ix1, *ix2) {
reduced_ops.push(new_op);
continue;
}
let new_reg = register_sequencer.next();
let new_ix = interference_graph.add_node(new_reg.clone());
for neighbor in interference_graph.neighbors(*ix1).collect::<Vec<_>>() {
interference_graph.add_edge(neighbor, new_ix, ());
}
for neighbor in interference_graph.neighbors(*ix2).collect::<Vec<_>>() {
if !interference_graph.contains_edge(neighbor, new_ix) {
interference_graph.add_edge(neighbor, new_ix, ());
}
}
interference_graph.remove_node(*ix1);
interference_graph.remove_node(*ix2);
reg_to_node_map.insert(new_reg.clone(), new_ix);
reg_to_node_map.insert(r1.clone(), new_ix);
reg_to_node_map.insert(r2.clone(), new_ix);
reg_to_reg_map.insert(r1.clone(), new_reg.clone());
reg_to_reg_map.insert(r2.clone(), new_reg.clone());
num_moves_removed += 1;
}
_ => {
reduced_ops.push(new_op);
}
}
}
_ => {
reduced_ops.push(new_op);
}
}
}
for new_op in &mut reduced_ops {
new_op.opcode = new_op.opcode.update_jump_immediate_values(&offset_map);
}
let mut final_reg_to_reg_map: HashMap<VirtualRegister, VirtualRegister> = HashMap::new();
for reg in reg_to_reg_map.keys() {
let mut temp = reg;
while let Some(t) = reg_to_reg_map.get(temp) {
temp = t;
}
final_reg_to_reg_map.insert(reg.clone(), temp.clone());
}
for new_op in &mut reduced_ops {
new_op.opcode = new_op.opcode.update_register(&final_reg_to_reg_map);
}
reduced_ops
}
pub(crate) fn color_interference_graph(
interference_graph: &mut InterferenceGraph,
) -> Vec<(VirtualRegister, BTreeSet<VirtualRegister>)> {
let mut stack: Vec<(VirtualRegister, BTreeSet<VirtualRegister>)> = vec![];
while let Some(node) = interference_graph.node_indices().next() {
let neighbors = interference_graph
.neighbors(node)
.map(|n| interference_graph[n].clone())
.collect();
stack.push((
interference_graph
.remove_node(node)
.expect("Node must exist"),
neighbors,
));
}
stack
}
pub(crate) fn assign_registers(
stack: &mut Vec<(VirtualRegister, BTreeSet<VirtualRegister>)>,
) -> RegisterPool {
let mut pool = RegisterPool::init();
while let Some((reg, neighbors)) = stack.pop() {
if matches!(reg, VirtualRegister::Virtual(_)) {
let available =
pool.registers
.iter_mut()
.find(|RegisterAllocationStatus { reg: _, used_by }| {
neighbors.intersection(used_by).count() == 0
});
if let Some(RegisterAllocationStatus { reg: _, used_by }) = available {
used_by.insert(reg.clone());
} else {
unimplemented!(
"The allocator cannot resolve a register mapping for this program.
This is a temporary artifact of the extremely early stage version of this language.
Try to lower the number of variables you use."
);
}
}
}
pool
}