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; ...@@ -19,48 +19,73 @@ use compiler::backend::reg_alloc::graph_coloring::petgraph::graph::NodeIndex;
const COALESCING : bool = true; const COALESCING : bool = true;
/// GraphColoring algorithm
/// based on Appel's book section 11.4
pub struct GraphColoring<'a> { pub struct GraphColoring<'a> {
// context
pub func: &'a mut MuFunctionVersion, pub func: &'a mut MuFunctionVersion,
pub cf: &'a mut CompiledFunction, pub cf: &'a mut CompiledFunction,
pub vm: &'a VM, pub vm: &'a VM,
pub ig: InterferenceGraph, pub ig: InterferenceGraph,
/// machine registers, preassigned a color
precolored: LinkedHashSet<NodeIndex>, precolored: LinkedHashSet<NodeIndex>,
/// all colors available
colors: LinkedHashMap<backend::RegGroup, LinkedHashSet<MuID>>, colors: LinkedHashMap<backend::RegGroup, LinkedHashSet<MuID>>,
pub colored_nodes: Vec<NodeIndex>, /// temporaries, not precolored and not yet processed
initial: Vec<NodeIndex>, initial: Vec<NodeIndex>,
degree: LinkedHashMap<NodeIndex, usize>, /// whether a temp is spillable
// FIXME: not used
worklist_moves: Vec<Move>, spillable: LinkedHashMap<MuID, bool>,
movelist: LinkedHashMap<NodeIndex, RefCell<Vec<Move>>>,
active_moves: LinkedHashSet<Move>, /// 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>, 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>, coalesced_moves: LinkedHashSet<Move>,
/// moves whose source and target interfere
constrained_moves: LinkedHashSet<Move>, 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>, alias: LinkedHashMap<NodeIndex, NodeIndex>,
worklist_spill: Vec<NodeIndex>,
spillable: LinkedHashMap<MuID, bool>,
spilled_nodes: Vec<NodeIndex>,
// for validation // for validation use
spill_history: LinkedHashMap<MuID, P<Value>>, // we need to log all registers get spilled with their spill location /// 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 spill_history: LinkedHashMap<MuID, P<Value>>,
/// we need to know the mapping between scratch temp -> original temp
worklist_freeze: LinkedHashSet<NodeIndex>, spill_scratch_temps: LinkedHashMap<MuID, MuID>
frozen_moves: LinkedHashSet<Move>,
worklist_simplify: LinkedHashSet<NodeIndex>,
select_stack: Vec<NodeIndex>
} }
impl <'a> GraphColoring<'a> { impl <'a> GraphColoring<'a> {
/// starts coloring
pub fn start (func: &'a mut MuFunctionVersion, cf: &'a mut CompiledFunction, vm: &'a VM) -> GraphColoring<'a> { 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) 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>>, fn start_with_spill_history(spill_history: LinkedHashMap<MuID, P<Value>>,
spill_scratch_temps: LinkedHashMap<MuID, MuID>, spill_scratch_temps: LinkedHashMap<MuID, MuID>,
func: &'a mut MuFunctionVersion, cf: &'a mut CompiledFunction, vm: &'a VM) -> GraphColoring<'a> func: &'a mut MuFunctionVersion, cf: &'a mut CompiledFunction, vm: &'a VM) -> GraphColoring<'a>
...@@ -74,9 +99,7 @@ impl <'a> GraphColoring<'a> { ...@@ -74,9 +99,7 @@ impl <'a> GraphColoring<'a> {
func: func, func: func,
cf: cf, cf: cf,
vm: vm, vm: vm,
ig: ig, ig: ig,
precolored: LinkedHashSet::new(), precolored: LinkedHashSet::new(),
colors: { colors: {
let mut map = LinkedHashMap::new(); let mut map = LinkedHashMap::new();
...@@ -85,10 +108,8 @@ impl <'a> GraphColoring<'a> { ...@@ -85,10 +108,8 @@ impl <'a> GraphColoring<'a> {
map map
}, },
colored_nodes: Vec::new(), colored_nodes: Vec::new(),
initial: Vec::new(), initial: Vec::new(),
degree: LinkedHashMap::new(), degree: LinkedHashMap::new(),
worklist_moves: Vec::new(), worklist_moves: Vec::new(),
movelist: LinkedHashMap::new(), movelist: LinkedHashMap::new(),
active_moves: LinkedHashSet::new(), active_moves: LinkedHashSet::new(),
...@@ -96,17 +117,13 @@ impl <'a> GraphColoring<'a> { ...@@ -96,17 +117,13 @@ impl <'a> GraphColoring<'a> {
coalesced_moves: LinkedHashSet::new(), coalesced_moves: LinkedHashSet::new(),
constrained_moves: LinkedHashSet::new(), constrained_moves: LinkedHashSet::new(),
alias: LinkedHashMap::new(), alias: LinkedHashMap::new(),
worklist_spill: Vec::new(), worklist_spill: Vec::new(),
spillable: LinkedHashMap::new(), spillable: LinkedHashMap::new(),
spilled_nodes: Vec::new(), spilled_nodes: Vec::new(),
spill_history: spill_history, spill_history: spill_history,
spill_scratch_temps: spill_scratch_temps, spill_scratch_temps: spill_scratch_temps,
worklist_freeze: LinkedHashSet::new(), worklist_freeze: LinkedHashSet::new(),
frozen_moves: LinkedHashSet::new(), frozen_moves: LinkedHashSet::new(),
worklist_simplify: LinkedHashSet::new(), worklist_simplify: LinkedHashSet::new(),
select_stack: Vec::new(), select_stack: Vec::new(),
}; };
...@@ -114,24 +131,29 @@ impl <'a> GraphColoring<'a> { ...@@ -114,24 +131,29 @@ impl <'a> GraphColoring<'a> {
coloring.regalloc() coloring.regalloc()
} }
/// returns formatted string for a node
fn display_node(&self, node: NodeIndex) -> String { fn display_node(&self, node: NodeIndex) -> String {
let id = self.ig.get_temp_of(node); let id = self.ig.get_temp_of(node);
self.display_id(id) self.display_id(id)
} }
/// returns formatted string for an ID
fn display_id(&self, id: MuID) -> String { fn display_id(&self, id: MuID) -> String {
self.func.context.get_temp_display(id) self.func.context.get_temp_display(id)
} }
/// returns formatted string for a move
fn display_move(&self, m: Move) -> String { fn display_move(&self, m: Move) -> String {
format!("Move: {} -> {}", self.display_node(m.from), self.display_node(m.to)) format!("Move: {} -> {}", self.display_node(m.from), self.display_node(m.to))
} }
/// does coloring register allocation
fn regalloc(mut self) -> GraphColoring<'a> { fn regalloc(mut self) -> GraphColoring<'a> {
trace!("---InterenceGraph---"); trace!("---InterenceGraph---");
let _p = hprof::enter("regalloc: graph coloring");
self.ig.print(&self.func.context); self.ig.print(&self.func.context);
// start timing for graph coloring
let _p = hprof::enter("regalloc: graph coloring");
// precolor for all machine registers // precolor for all machine registers
for reg in backend::all_regs().values() { for reg in backend::all_regs().values() {
...@@ -146,20 +168,22 @@ impl <'a> GraphColoring<'a> { ...@@ -146,20 +168,22 @@ impl <'a> GraphColoring<'a> {
let group = backend::pick_group_for_reg(reg_id); let group = backend::pick_group_for_reg(reg_id);
self.colors.get_mut(&group).unwrap().insert(reg_id); self.colors.get_mut(&group).unwrap().insert(reg_id);
} }
// push uncolored nodes to initial work set
for node in self.ig.nodes() { for node in self.ig.nodes() {
if !self.ig.is_colored(node) { if !self.ig.is_colored(node) {
self.initial.push(node); self.initial.push(node);
let outdegree = self.ig.outdegree_of(node); let degree = self.ig.get_degree_of(node);
self.degree.insert(node, outdegree); self.degree.insert(node, degree);
trace!("{} has a degree of {}", self.display_node(node), degree);
trace!("{} has a degree of {}", self.display_node(node), outdegree);
} }
} }
// initialize work
self.build(); self.build();
self.make_work_list(); self.make_work_list();
// main loop
while { while {
if !self.worklist_simplify.is_empty() { if !self.worklist_simplify.is_empty() {
self.simplify(); self.simplify();
...@@ -176,11 +200,14 @@ impl <'a> GraphColoring<'a> { ...@@ -176,11 +200,14 @@ impl <'a> GraphColoring<'a> {
&& self.worklist_freeze.is_empty() && self.worklist_freeze.is_empty()
&& self.worklist_spill.is_empty()) && self.worklist_spill.is_empty())
} {} } {}
// pick color for nodes
self.assign_colors(); self.assign_colors();
// finish
drop(_p); drop(_p);
// if we need to spill
if !self.spilled_nodes.is_empty() { if !self.spilled_nodes.is_empty() {
trace!("spill required"); trace!("spill required");
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
...@@ -190,8 +217,10 @@ impl <'a> GraphColoring<'a> { ...@@ -190,8 +217,10 @@ impl <'a> GraphColoring<'a> {
} }
} }
// rewrite program to insert spilling code
self.rewrite_program(); self.rewrite_program();
// recursively redo graph coloring
return GraphColoring::start_with_spill_history(self.spill_history.clone(), self.spill_scratch_temps.clone(), self.func, self.cf, self.vm); return GraphColoring::start_with_spill_history(self.spill_history.clone(), self.spill_scratch_temps.clone(), self.func, self.cf, self.vm);
} }
...@@ -221,7 +250,7 @@ impl <'a> GraphColoring<'a> { ...@@ -221,7 +250,7 @@ impl <'a> GraphColoring<'a> {
if { if {
// condition: degree >= K // 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); let n_regs = self.n_regs_for_node(node);
degree >= n_regs degree >= n_regs
...@@ -306,7 +335,7 @@ impl <'a> GraphColoring<'a> { ...@@ -306,7 +335,7 @@ impl <'a> GraphColoring<'a> {
let mut adj = LinkedHashSet::new(); let mut adj = LinkedHashSet::new();
// add n's successors // add n's successors
for s in self.ig.outedges_of(n) { for s in self.ig.get_edges_of(n) {
adj.insert(s); adj.insert(s);
} }
...@@ -626,7 +655,7 @@ impl <'a> GraphColoring<'a> { ...@@ -626,7 +655,7 @@ impl <'a> GraphColoring<'a> {
trace!("all the colors for this temp: {:?}", ok_colors); 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); let w_alias = self.get_alias(w);
match self.ig.get_color_of(w_alias) { match self.ig.get_color_of(w_alias) {
None => {}, // do nothing None => {}, // do nothing
......
...@@ -3,10 +3,9 @@ extern crate petgraph; ...@@ -3,10 +3,9 @@ extern crate petgraph;
mod liveness; mod liveness;
mod coloring; mod coloring;
pub use compiler::backend::reg_alloc::graph_coloring::liveness::InterferenceGraph; use compiler::backend::reg_alloc::graph_coloring::liveness::InterferenceGraph;
//pub use compiler::backend::reg_alloc::graph_coloring::liveness::build as build_inteference_graph; use compiler::backend::reg_alloc::graph_coloring::liveness::build_interference_graph_chaitin_briggs as build_inteference_graph;
pub use compiler::backend::reg_alloc::graph_coloring::liveness::build_chaitin_briggs as build_inteference_graph; use compiler::backend::reg_alloc::graph_coloring::coloring::GraphColoring;
pub use compiler::backend::reg_alloc::graph_coloring::coloring::GraphColoring;
use ast::ir::*; use ast::ir::*;
use vm::VM; use vm::VM;
...@@ -20,6 +19,20 @@ pub struct RegisterAllocation { ...@@ -20,6 +19,20 @@ pub struct RegisterAllocation {
name: &'static str, name: &'static str,
} }
impl CompilerPass for RegisterAllocation {
fn name(&self) -> &'static str {
self.name
}
fn as_any(&self) -> &Any {
self
}
fn visit_function(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
self.coloring(vm, func);
}
}
impl RegisterAllocation { impl RegisterAllocation {
pub fn new() -> RegisterAllocation { pub fn new() -> RegisterAllocation {
RegisterAllocation { RegisterAllocation {
...@@ -27,47 +40,54 @@ impl RegisterAllocation { ...@@ -27,47 +40,54 @@ impl RegisterAllocation {
} }
} }
#[allow(unused_variables)]
fn coloring(&mut self, vm: &VM, func: &mut MuFunctionVersion) { fn coloring(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
// get compiled function
let compiled_funcs = vm.compiled_funcs().read().unwrap(); let compiled_funcs = vm.compiled_funcs().read().unwrap();
let mut cf = compiled_funcs.get(&func.id()).unwrap().write().unwrap(); let mut cf = compiled_funcs.get(&func.id()).unwrap().write().unwrap();
// 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); init_machine_regs_for_func(&mut func.context);
// do graph coloring
let coloring = GraphColoring::start(func, &mut cf, vm); let coloring = GraphColoring::start(func, &mut cf, vm);
// if we need to validate the results
if !vm.vm_options.flag_disable_regalloc_validate { if !vm.vm_options.flag_disable_regalloc_validate {
// a map of register assignment (from temp to machine register)
let reg_assignment = coloring.get_assignments(); 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(); let spill_scratch_temps = coloring.get_spill_scratch_temps();
validate::validate_regalloc(&coloring.cf, reg_assignment, spill_scratch_temps); validate::validate_regalloc(&coloring.cf, reg_assignment, spill_scratch_temps);
} }
// replace regs // use the result to replace temporaries with assigned regs
trace!("Replacing Registers..."); trace!("Replacing Registers...");
for (temp, machine_reg) in coloring.get_assignments() { for (temp, machine_reg) in coloring.get_assignments() {
trace!("replacing {} with {}", temp, machine_reg); trace!("replacing {} with {}", temp, machine_reg);
coloring.cf.mc_mut().replace_reg(temp, machine_reg); coloring.cf.mc_mut().replace_reg(temp, machine_reg);
coloring.cf.temps.insert(temp, machine_reg); coloring.cf.temps.insert(temp, machine_reg);
} }
// find out what callee saved registers are used // find out what callee saved registers are used, so we can delete unnecessary savings.
// FIXME: current not doing this // Currently I am only deleting those unnecessary push/pops of callee saved regs, but
// reason: we generated frame slots for callee saved registers, then generated slots for spills // I am not deleting the frame slots for them (so the frame size is still larger than
// if we delete some callee saved registers, the slots for spills are not correct // it needs to be).
// FIXME: should fix offsets of frame slots, and patch the code. See Issue #47
{ {
use std::collections::HashSet; // all the used callee saved registers
let used_callee_saved: Vec<MuID> = {
let used_callee_saved: HashSet<MuID> = use std::collections::HashSet;
coloring.cf.temps.values() let used_callee_saved: HashSet<MuID> =
.map(|x| *x) coloring.cf.temps.values()
.filter(|x| is_callee_saved(*x)) .map(|x| *x)
.collect(); .filter(|x| is_callee_saved(*x))
.collect();
let used_callee_saved: Vec<MuID> = used_callee_saved.into_iter().collect(); used_callee_saved.into_iter().collect()
};
// remove unused callee saved registers
let removed_callee_saved = coloring.cf.mc_mut().remove_unnecessary_callee_saved(used_callee_saved); let removed_callee_saved = coloring.cf.mc_mut().remove_unnecessary_callee_saved(used_callee_saved);
for reg in removed_callee_saved { for reg in removed_callee_saved {
coloring.cf.frame.remove_record_for_callee_saved_reg(reg); coloring.cf.frame.remove_record_for_callee_saved_reg(reg);
...@@ -82,17 +102,3 @@ impl RegisterAllocation { ...@@ -82,17 +102,3 @@ impl RegisterAllocation {
coloring.cf.mc().trace_mc(); coloring.cf.mc().trace_mc();
} }
} }
impl CompilerPass for RegisterAllocation {
fn name(&self) -> &'static str {
self.name
}
fn as_any(&self) -> &Any {
self
}
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; 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; mod validate;
/// exposing graph coloring register allocation pass.
pub use compiler::backend::reg_alloc::graph_coloring::RegisterAllocation; 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