Commit 7ab20356 authored by qinsoon's avatar qinsoon

[wip] reg alloc

parent b4e69df4
......@@ -19,48 +19,73 @@ use compiler::backend::reg_alloc::graph_coloring::petgraph::graph::NodeIndex;
const COALESCING : bool = true;
/// GraphColoring algorithm
/// based on Appel's book section 11.4
pub struct GraphColoring<'a> {
// context
pub func: &'a mut MuFunctionVersion,
pub cf: &'a mut CompiledFunction,
pub vm: &'a VM,
pub ig: InterferenceGraph,
/// machine registers, preassigned a color
precolored: LinkedHashSet<NodeIndex>,
/// all colors available
colors: LinkedHashMap<backend::RegGroup, LinkedHashSet<MuID>>,
pub colored_nodes: Vec<NodeIndex>,
/// temporaries, not precolored and not yet processed
initial: Vec<NodeIndex>,
degree: LinkedHashMap<NodeIndex, usize>,
worklist_moves: Vec<Move>,
movelist: LinkedHashMap<NodeIndex, RefCell<Vec<Move>>>,
active_moves: LinkedHashSet<Move>,
coalesced_nodes: LinkedHashSet<NodeIndex>,
coalesced_moves: LinkedHashSet<Move>,
constrained_moves: LinkedHashSet<Move>,
alias: LinkedHashMap<NodeIndex, NodeIndex>,
/// whether a temp is spillable
// FIXME: not used
spillable: LinkedHashMap<MuID, bool>,
/// list of low-degree non-move-related nodes
worklist_simplify: LinkedHashSet<NodeIndex>,
/// low-degree move related nodes
worklist_freeze: LinkedHashSet<NodeIndex>,
/// nodes marked for spilling during this round
worklist_spill: Vec<NodeIndex>,
spillable: LinkedHashMap<MuID, bool>,
/// nodes marked for spilling during this round
spilled_nodes: Vec<NodeIndex>,
/// temps that have been coalesced
/// when u <- v is coalesced, v is added to this set and u put back on some work list
coalesced_nodes: LinkedHashSet<NodeIndex>,
/// nodes successfully colored
colored_nodes: Vec<NodeIndex>,
/// stack containing temporaries removed from the graph
select_stack: Vec<NodeIndex>,
// for validation
spill_history: LinkedHashMap<MuID, P<Value>>, // we need to log all registers get spilled with their spill location
spill_scratch_temps: LinkedHashMap<MuID, MuID>, // we need to know the mapping between scratch temp -> original temp
worklist_freeze: LinkedHashSet<NodeIndex>,
/// moves that have been coalesced
coalesced_moves: LinkedHashSet<Move>,
/// moves whose source and target interfere
constrained_moves: LinkedHashSet<Move>,
/// moves that will no longer be considered for coalescing
frozen_moves: LinkedHashSet<Move>,
/// moves enabled for possible coalescing
worklist_moves: Vec<Move>,
/// moves not yet ready for coalescing
active_moves: LinkedHashSet<Move>,
worklist_simplify: LinkedHashSet<NodeIndex>,
select_stack: Vec<NodeIndex>
/// degree of nodes
degree: LinkedHashMap<NodeIndex, usize>,
/// a mapping from a node to the list of moves it is associated with
movelist: LinkedHashMap<NodeIndex, RefCell<Vec<Move>>>,
/// when a move (u, v) has been coalesced, and v put in coalescedNodes, then alias(v) = u
alias: LinkedHashMap<NodeIndex, NodeIndex>,
// for validation use
/// we need to log all registers get spilled with their spill location
spill_history: LinkedHashMap<MuID, P<Value>>,
/// we need to know the mapping between scratch temp -> original temp
spill_scratch_temps: LinkedHashMap<MuID, MuID>
impl <'a> GraphColoring<'a> {
/// starts coloring
pub fn start (func: &'a mut MuFunctionVersion, cf: &'a mut CompiledFunction, vm: &'a VM) -> GraphColoring<'a> {
GraphColoring::start_with_spill_history(LinkedHashMap::new(), LinkedHashMap::new(), func, cf, vm)
/// restarts coloring with spill history
fn start_with_spill_history(spill_history: LinkedHashMap<MuID, P<Value>>,
spill_scratch_temps: LinkedHashMap<MuID, MuID>,
func: &'a mut MuFunctionVersion, cf: &'a mut CompiledFunction, vm: &'a VM) -> GraphColoring<'a>
......@@ -74,9 +99,7 @@ impl <'a> GraphColoring<'a> {
func: func,
cf: cf,
vm: vm,
ig: ig,
precolored: LinkedHashSet::new(),
colors: {
let mut map = LinkedHashMap::new();
......@@ -85,10 +108,8 @@ impl <'a> GraphColoring<'a> {
colored_nodes: Vec::new(),
initial: Vec::new(),
degree: LinkedHashMap::new(),
worklist_moves: Vec::new(),
movelist: LinkedHashMap::new(),
active_moves: LinkedHashSet::new(),
......@@ -96,17 +117,13 @@ impl <'a> GraphColoring<'a> {
coalesced_moves: LinkedHashSet::new(),
constrained_moves: LinkedHashSet::new(),
alias: LinkedHashMap::new(),
worklist_spill: Vec::new(),
spillable: LinkedHashMap::new(),
spilled_nodes: Vec::new(),
spill_history: spill_history,
spill_scratch_temps: spill_scratch_temps,
worklist_freeze: LinkedHashSet::new(),
frozen_moves: LinkedHashSet::new(),
worklist_simplify: LinkedHashSet::new(),
select_stack: Vec::new(),
......@@ -114,25 +131,30 @@ impl <'a> GraphColoring<'a> {
/// returns formatted string for a node
fn display_node(&self, node: NodeIndex) -> String {
let id = self.ig.get_temp_of(node);
/// returns formatted string for an ID
fn display_id(&self, id: MuID) -> String {
/// returns formatted string for a move
fn display_move(&self, m: Move) -> String {
format!("Move: {} -> {}", self.display_node(m.from), self.display_node(
/// does coloring register allocation
fn regalloc(mut self) -> GraphColoring<'a> {
let _p = hprof::enter("regalloc: graph coloring");
// start timing for graph coloring
let _p = hprof::enter("regalloc: graph coloring");
// precolor for all machine registers
for reg in backend::all_regs().values() {
let reg_id = reg.extract_ssa_id().unwrap();
......@@ -147,19 +169,21 @@ impl <'a> GraphColoring<'a> {
// push uncolored nodes to initial work set
for node in self.ig.nodes() {
if !self.ig.is_colored(node) {
let outdegree = self.ig.outdegree_of(node);, outdegree);
trace!("{} has a degree of {}", self.display_node(node), outdegree);
let degree = self.ig.get_degree_of(node);, degree);
trace!("{} has a degree of {}", self.display_node(node), degree);
// initialize work;
// main loop
while {
if !self.worklist_simplify.is_empty() {
......@@ -177,10 +201,13 @@ impl <'a> GraphColoring<'a> {
&& self.worklist_spill.is_empty())
} {}
// pick color for nodes
// finish
// if we need to spill
if !self.spilled_nodes.is_empty() {
trace!("spill required");
if cfg!(debug_assertions) {
......@@ -190,8 +217,10 @@ impl <'a> GraphColoring<'a> {
// rewrite program to insert spilling code
// recursively redo graph coloring
return GraphColoring::start_with_spill_history(self.spill_history.clone(), self.spill_scratch_temps.clone(), self.func,, self.vm);
......@@ -221,7 +250,7 @@ impl <'a> GraphColoring<'a> {
if {
// condition: degree >= K
let degree = self.ig.degree_of(node);
let degree = self.ig.get_degree_of(node);
let n_regs = self.n_regs_for_node(node);
degree >= n_regs
......@@ -306,7 +335,7 @@ impl <'a> GraphColoring<'a> {
let mut adj = LinkedHashSet::new();
// add n's successors
for s in self.ig.outedges_of(n) {
for s in self.ig.get_edges_of(n) {
......@@ -626,7 +655,7 @@ impl <'a> GraphColoring<'a> {
trace!("all the colors for this temp: {:?}", ok_colors);
for w in self.ig.outedges_of(n) {
for w in self.ig.get_edges_of(n) {
let w_alias = self.get_alias(w);
match self.ig.get_color_of(w_alias) {
None => {}, // do nothing
......@@ -3,10 +3,9 @@ extern crate petgraph;
mod liveness;
mod coloring;
pub use compiler::backend::reg_alloc::graph_coloring::liveness::InterferenceGraph;
//pub use compiler::backend::reg_alloc::graph_coloring::liveness::build as build_inteference_graph;
pub use compiler::backend::reg_alloc::graph_coloring::liveness::build_chaitin_briggs as build_inteference_graph;
pub use compiler::backend::reg_alloc::graph_coloring::coloring::GraphColoring;
use compiler::backend::reg_alloc::graph_coloring::liveness::InterferenceGraph;
use compiler::backend::reg_alloc::graph_coloring::liveness::build_interference_graph_chaitin_briggs as build_inteference_graph;
use compiler::backend::reg_alloc::graph_coloring::coloring::GraphColoring;
use ast::ir::*;
use vm::VM;
......@@ -20,6 +19,20 @@ pub struct RegisterAllocation {
name: &'static str,
impl CompilerPass for RegisterAllocation {
fn name(&self) -> &'static str {
fn as_any(&self) -> &Any {
fn visit_function(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
self.coloring(vm, func);
impl RegisterAllocation {
pub fn new() -> RegisterAllocation {
RegisterAllocation {
......@@ -27,47 +40,54 @@ impl RegisterAllocation {
fn coloring(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
// get compiled function
let compiled_funcs = vm.compiled_funcs().read().unwrap();
let mut cf = compiled_funcs.get(&;
// initialize machine registers for the function context
// initialize machine registers for the function context (we are gonna use them)
init_machine_regs_for_func(&mut func.context);
// do graph coloring
let coloring = GraphColoring::start(func, &mut cf, vm);
// if we need to validate the results
if !vm.vm_options.flag_disable_regalloc_validate {
// a map of register assignment (from temp to machine register)
let reg_assignment = coloring.get_assignments();
// a map of spilled temporaries (from spilled temp to scratch temp)
// we use this to validate spilling correctness
let spill_scratch_temps = coloring.get_spill_scratch_temps();
validate::validate_regalloc(&, reg_assignment, spill_scratch_temps);
// replace regs
// use the result to replace temporaries with assigned regs
trace!("Replacing Registers...");
for (temp, machine_reg) in coloring.get_assignments() {
trace!("replacing {} with {}", temp, machine_reg);, machine_reg);, machine_reg);
// find out what callee saved registers are used
// FIXME: current not doing this
// reason: we generated frame slots for callee saved registers, then generated slots for spills
// if we delete some callee saved registers, the slots for spills are not correct
// find out what callee saved registers are used, so we can delete unnecessary savings.
// Currently I am only deleting those unnecessary push/pops of callee saved regs, but
// I am not deleting the frame slots for them (so the frame size is still larger than
// it needs to be).
// FIXME: should fix offsets of frame slots, and patch the code. See Issue #47
// all the used callee saved registers
let used_callee_saved: Vec<MuID> = {
use std::collections::HashSet;
let used_callee_saved: HashSet<MuID> =
.map(|x| *x)
.filter(|x| is_callee_saved(*x))
let used_callee_saved: Vec<MuID> = used_callee_saved.into_iter().collect();
// remove unused callee saved registers
let removed_callee_saved =;
for reg in removed_callee_saved {;
......@@ -82,17 +102,3 @@ impl RegisterAllocation {;
impl CompilerPass for RegisterAllocation {
fn name(&self) -> &'static str {
fn as_any(&self) -> &Any {
fn visit_function(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
self.coloring(vm, func);
//! This module is for register allocation.
//! We should encapsulate the details of register allocation within this module,
//! and expose RegisterAllocation (which implements CompilerPass) to the compiler.
//! Outside of this module, the compiler should not assume a specific register
//! allocation algorithm.
//! We currently implemented graph coloring. We may have other algorithms in
//! the future.
/// a graph coloring implementation (mostly based on Appel's compiler book).
pub mod graph_coloring;
/// a register allocation validation pass. Design is discussed in Issue #19.
/// This pass is controlled by --disable-regalloc-validate option
/// (currently disabled for all cases due to bugs)
mod validate;
/// exposing graph coloring register allocation pass.
pub use compiler::backend::reg_alloc::graph_coloring::RegisterAllocation;
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment