use std::collections::{BTreeSet, HashMap};
use crate::compiler::ir::block::ControlFlowGraph;
use crate::compiler::ir::liveness::LivenessInfo;
use crate::compiler::ir::types::IrVar;
#[derive(Debug, Clone)]
pub struct RegisterAllocation {
pub mapping: HashMap<IrVar, u8>,
pub max_registers: u8,
}
impl RegisterAllocation {
pub fn get(&self, var: IrVar) -> Option<u8> {
self.mapping.get(&var).copied()
}
}
pub fn allocate_registers(
cfg: &ControlFlowGraph,
num_params: u8,
) -> Result<RegisterAllocation, String> {
let liveness = LivenessInfo::compute(cfg);
let intervals_map = liveness.compute_live_intervals(cfg);
let mut intervals: Vec<_> = intervals_map.into_values().collect();
let captured = crate::compiler::ir::opt::captured_vars(cfg);
for interval in &mut intervals {
if captured.contains(&interval.var) {
interval.end = usize::MAX;
}
}
intervals.sort_by_key(|intv| (intv.start, intv.end));
let mut mapping: HashMap<IrVar, u8> = HashMap::new();
for i in 0..num_params {
mapping.insert(IrVar(i as u32), i);
}
let mut free_registers: BTreeSet<u8> = (num_params..255).collect();
let mut active: Vec<(usize, u8, IrVar)> = Vec::new();
let mut max_reg = num_params;
for interval in intervals {
if mapping.contains_key(&interval.var) {
continue;
}
active.retain(|&(end, reg, _var)| {
if end < interval.start {
free_registers.insert(reg);
false
} else {
true
}
});
if let Some(®) = free_registers.iter().next() {
free_registers.remove(®);
mapping.insert(interval.var, reg);
active.push((interval.end, reg, interval.var));
if reg.saturating_add(1) > max_reg {
max_reg = reg.saturating_add(1);
}
} else {
return Err(format!(
"register allocation failure: register limit (255) exceeded for variable {:?}",
interval.var
));
}
}
Ok(RegisterAllocation {
mapping,
max_registers: max_reg,
})
}