GitLab will be upgraded to the 12.10.14-ce.0 on 28 Sept 2020 at 2.00pm (AEDT) to 2.30pm (AEDT). During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to us at N110 (b) CSIT building.

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>,
/// 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>,
/// 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>,
/// 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>,
/// 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>,
worklist_spill: Vec<NodeIndex>,
spillable: LinkedHashMap<MuID, bool>,
spilled_nodes: 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>,
frozen_moves: LinkedHashSet<Move>,
worklist_simplify: LinkedHashSet<NodeIndex>,
select_stack: Vec<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,24 +131,29 @@ 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() {
......@@ -146,20 +168,22 @@ impl <'a> GraphColoring<'a> {
let group = backend::pick_group_for_reg(reg_id);
// 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() {
......@@ -176,11 +200,14 @@ impl <'a> GraphColoring<'a> {
&& self.worklist_freeze.is_empty()
&& 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
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();
// 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))
// 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
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment