GitLab will be upgraded on June 2nd 2020 at 2.00 pm (AEDT) to 3.00 pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to local Gitlab admin team.

Commit 19c5b2c0 authored by qinsoon's avatar qinsoon

[wip] backbone of graph coloring

parent d570c55c
......@@ -12,6 +12,7 @@ pub use compiler::backend::x86_64::asm_backend::ASMCodeGen;
use ast::ptr::P;
use ast::ir::*;
use ast::types::*;
use compiler::backend::RegGroup;
macro_rules! GPR {
($name: expr, $id: expr) => {
......@@ -79,6 +80,25 @@ lazy_static! {
R14.clone(),
R15.clone()
];
pub static ref ALL_GPRs : [P<Value>; 16] = [
RAX.clone(),
RCX.clone(),
RDX.clone(),
RBX.clone(),
RSP.clone(),
RBP.clone(),
RSI.clone(),
RDI.clone(),
R8.clone(),
R9.clone(),
R10.clone(),
R11.clone(),
R12.clone(),
R13.clone(),
R14.clone(),
R15.clone()
];
}
lazy_static!{
......@@ -114,6 +134,25 @@ lazy_static!{
];
pub static ref CALLEE_SAVED_FPRs : [P<Value>; 0] = [];
pub static ref ALL_FPRs : [P<Value>; 16] = [
XMM0.clone(),
XMM1.clone(),
XMM2.clone(),
XMM3.clone(),
XMM4.clone(),
XMM5.clone(),
XMM6.clone(),
XMM7.clone(),
XMM8.clone(),
XMM9.clone(),
XMM10.clone(),
XMM11.clone(),
XMM12.clone(),
XMM13.clone(),
XMM14.clone(),
XMM15.clone(),
];
}
pub const GPR_COUNT : usize = 16;
......@@ -156,17 +195,39 @@ lazy_static! {
];
}
pub fn get_name_for_value(id: MuID, func_context: &FunctionContext) -> &'static str {
if id < RESERVED_NODE_IDS_FOR_MACHINE {
ALL_MACHINE_REGs[id].tag
} else {
match func_context.get_value(id) {
Some(ref v) => v.tag,
None => panic!("cannot find tag for node id={}", id)
}
pub fn init_machine_regs_for_func (func_context: &mut FunctionContext) {
use std::cell::Cell;
for reg in ALL_MACHINE_REGs.iter() {
let reg_id = reg.extract_ssa_id().unwrap();
let entry = SSAVarEntry {
id: reg_id,
tag: reg.tag,
ty: reg.ty.clone(),
use_count: Cell::new(0),
expr: None
};
func_context.value_tags.insert(reg.tag, reg_id);
func_context.values.insert(reg_id, entry);
}
}
pub fn number_of_regs_in_group(group: RegGroup) -> usize {
match group {
RegGroup::GPR => ALL_GPRs.len(),
RegGroup::FPR => ALL_FPRs.len()
}
}
pub fn number_of_all_regs() -> usize {
ALL_MACHINE_REGs.len()
}
pub fn all_regs() -> &'static Vec<P<Value>> {
&ALL_MACHINE_REGs
}
pub fn is_valid_x86_imm(op: &P<Value>) -> bool {
use std::u32;
match op.v {
......
pub mod inst_sel;
pub mod reg_alloc;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RegGroup {GPR, FPR}
#[cfg(target_arch = "x86_64")]
#[path = "arch/x86_64/mod.rs"]
mod x86_64;
#[cfg(target_arch = "arm")]
#[path = "arch/arm/mod.rs"]
mod arm;
#[cfg(target_arch = "x86_64")]
pub use compiler::backend::x86_64::get_name_for_value;
pub use compiler::backend::x86_64::init_machine_regs_for_func;
#[cfg(target_arch = "x86_64")]
pub use compiler::backend::x86_64::GPR_COUNT;
pub use compiler::backend::x86_64::number_of_regs_in_group;
#[cfg(target_arch = "x86_64")]
pub use compiler::backend::x86_64::FPR_COUNT;
pub use compiler::backend::x86_64::number_of_all_regs;
#[cfg(target_arch = "x86_64")]
pub use compiler::backend::x86_64::all_regs;
#[cfg(target_arch = "arm")]
#[path = "arch/arm/mod.rs"]
mod arm;
#[cfg(target_arch = "arm")]
pub use compiler::backend::arm::GPR_COUNT;
......
use compiler::backend::reg_alloc::liveness::InterferenceGraph;
use ast::ir::MuID;
use compiler::backend::reg_alloc::liveness::InterferenceGraph;
use compiler::backend::reg_alloc::liveness::{Node, Move};
use vm::machine_code::CompiledFunction;
use compiler::backend::GPR_COUNT;
use compiler::backend;
use compiler::backend::reg_alloc::liveness::find_value;
use std::collections::LinkedList;
use std::collections::HashSet;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
const COALESCING : AtomicBool = ATOMIC_BOOL_INIT;
pub struct GraphColoring <'a> {
ig: InterferenceGraph,
cur_cf: &'a CompiledFunction,
K: usize
precolored: HashSet<Node>,
colors: HashSet<MuID>,
initial: LinkedList<Node>,
degree: HashMap<Node, usize>,
worklist_moves: LinkedList<Move>,
movelist: HashMap<Node, Vec<Move>>,
active_moves: HashSet<Move>,
worklist_spill: LinkedList<Node>,
worklist_freeze: HashSet<Node>,
worklist_simplify: HashSet<Node>
}
impl <'a> GraphColoring <'a> {
......@@ -14,13 +37,151 @@ impl <'a> GraphColoring <'a> {
let mut coloring = GraphColoring {
ig: ig,
cur_cf: cf,
K: 0
precolored: HashSet::new(),
colors: HashSet::new(),
initial: LinkedList::new(),
degree: HashMap::new(),
worklist_moves: LinkedList::new(),
movelist: HashMap::new(),
active_moves: HashSet::new(),
worklist_spill: LinkedList::new(),
worklist_freeze: HashSet::new(),
worklist_simplify: HashSet::new()
};
coloring.init();
}
fn init (&mut self) {
self.K = GPR_COUNT;
COALESCING.store(true, Ordering::Relaxed);
for reg in backend::all_regs().iter() {
let reg_id = reg.extract_ssa_id().unwrap();
let node = self.ig.get_node(reg_id);
self.precolored.insert(node);
self.colors.insert(reg_id);
}
for node in self.ig.nodes() {
if !self.ig.is_colored(node) {
self.initial.push_back(node);
self.degree.insert(node, self.ig.outdegree_of(node));
}
}
self.build();
self.make_work_list();
while {
if !self.worklist_simplify.is_empty() {
self.simplify();
} else if !self.worklist_moves.is_empty() {
self.coalesce();
} else if !self.worklist_freeze.is_empty() {
self.freeze();
} else if !self.worklist_spill.is_empty() {
self.select_spill();
}
! (self.worklist_simplify.is_empty()
&& self.worklist_moves.is_empty()
&& self.worklist_freeze.is_empty()
&& self.worklist_spill.is_empty())
} {}
self.assign_colors();
}
fn simplify(&mut self) {
unimplemented!()
}
fn coalesce(&mut self) {
unimplemented!()
}
fn freeze(&mut self) {
unimplemented!()
}
fn select_spill(&mut self) {
unimplemented!()
}
fn assign_colors(&mut self) {
unimplemented!()
}
fn build(&mut self) {
if COALESCING.load(Ordering::Relaxed) {
let ref ig = self.ig;
let ref mut movelist = self.movelist;
for m in ig.moves() {
self.worklist_moves.push_back(m.clone());
GraphColoring::movelist_mut(movelist, m.from).push(m.clone());
GraphColoring::movelist_mut(movelist, m.to).push(m.clone());
}
}
}
fn make_work_list(&mut self) {
while !self.initial.is_empty() {
let node = self.initial.pop_front().unwrap();
if {
// condition: degree >= K
let degree = self.ig.degree_of(node);
let n_regs = backend::number_of_regs_in_group(self.ig.get_group_of(node));
degree >= n_regs
} {
self.worklist_spill.push_back(node);
} else if self.is_move_related(node) {
self.worklist_freeze.insert(node);
} else {
self.worklist_simplify.insert(node);
}
}
}
fn is_move_related(&mut self, node: Node) -> bool {
!self.node_moves(node).is_empty()
}
fn node_moves(&mut self, node: Node) -> HashSet<Move> {
let mut moves = HashSet::new();
// addAll(active_moves)
for m in self.active_moves.iter() {
moves.insert(m.clone());
}
// addAll(worklist_moves)
for m in self.worklist_moves.iter() {
moves.insert(m.clone());
}
let mut retained = HashSet::new();
let movelist = GraphColoring::movelist_mut(&mut self.movelist, node);
for m in moves.iter() {
if find_value(movelist, *m).is_some() {
retained.insert(*m);
}
}
retained
}
fn movelist_mut(list: &mut HashMap<Node, Vec<Move>>, node: Node) -> &mut Vec<Move> {
if !list.contains_key(&node) {
list.insert(node, Vec::new());
}
list.get_mut(&node).unwrap()
}
}
\ No newline at end of file
......@@ -3,7 +3,8 @@ extern crate nalgebra;
use vm::machine_code::CompiledFunction;
use vm::machine_code::MachineCode;
use ast::ir::*;
use compiler::backend::get_name_for_value as get_tag;
use ast::types;
use compiler::backend;
use std::collections::LinkedList;
use std::collections::{HashMap, HashSet};
......@@ -12,64 +13,119 @@ use self::nalgebra::DMatrix;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Node(usize);
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct NodeProperty {
color: Option<MuID>,
group: backend::RegGroup
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Move{pub from: Node, pub to: Node}
pub struct InterferenceGraph {
nodes: HashMap<MuID, Node>,
nodes_property: HashMap<Node, NodeProperty>,
matrix: Option<DMatrix<bool>>,
color: HashMap<Node, MuID>,
moves: HashSet<(MuID, MuID)>
moves: HashSet<Move>,
}
impl InterferenceGraph {
fn new() -> InterferenceGraph {
InterferenceGraph {
nodes: HashMap::new(),
nodes_property: HashMap::new(),
matrix: None,
color: HashMap::new(),
moves: HashSet::new()
}
}
fn new_node(&mut self, reg: MuID) -> Node {
if !self.nodes.contains_key(&reg) {
fn new_node(&mut self, reg_id: MuID, context: &FunctionContext) -> Node {
let entry = context.get_value(reg_id).unwrap();
if !self.nodes.contains_key(&reg_id) {
let index = self.nodes.len();
let node = Node(index);
self.nodes.insert(reg, node.clone());
// add the node
self.nodes.insert(reg_id, node.clone());
// add node property
let group = {
let ref ty = entry.ty;
if types::is_scalar(ty) {
if types::is_fp(ty) {
backend::RegGroup::GPR
} else {
backend::RegGroup::FPR
}
} else {
unimplemented!()
}
};
let property = NodeProperty {
color: None,
group: group
};
self.nodes_property.insert(node, property);
node
} else {
* self.nodes.get(&reg).unwrap()
* self.nodes.get(&reg_id).unwrap()
}
}
fn get_node(&self, reg: MuID) -> Node {
pub fn get_node(&self, reg: MuID) -> Node {
match self.nodes.get(&reg) {
Some(index) => *index,
None => panic!("do not have a node for {}", reg)
}
}
pub fn nodes(&self) -> Vec<Node> {
let mut ret = vec![];
for node in self.nodes.values() {
ret.push(node.clone());
}
ret
}
pub fn moves(&self) -> &HashSet<Move> {
&self.moves
}
pub fn n_nodes(&self) -> usize {
self.nodes.len()
}
fn init_graph(&mut self) {
let len = self.nodes.len();
self.matrix = Some(DMatrix::from_element(len, len, false));
}
fn add_move(&mut self, src: MuID, dst: MuID) {
self.moves.insert((src, dst));
fn add_move(&mut self, src: Node, dst: Node) {
self.moves.insert(Move{from: src, to: dst});
}
fn add_interference_edge(&mut self, from: Node, to: Node) {
self.matrix.as_mut().unwrap()[(from.0, to.0)] = true;
// only if two nodes are from the same RegGroup,
// they may interefere
if self.nodes_property.get(&from).unwrap().group
== self.nodes_property.get(&to).unwrap().group {
self.matrix.as_mut().unwrap()[(from.0, to.0)] = true;
}
}
fn color_node(&mut self, node: Node, color: MuID) {
self.color.insert(node, color);
self.nodes_property.get_mut(&node).unwrap().color = Some(color);
}
fn node_has_color(&self, node: Node) -> bool {
self.color.contains_key(&node)
pub fn is_colored(&self, node: Node) -> bool {
self.nodes_property.get(&node).unwrap().color.is_some()
}
pub fn get_group_of(&self, node: Node) -> backend::RegGroup {
self.nodes_property.get(&node).unwrap().group
}
fn is_same_node(&self, node1: Node, node2: Node) -> bool {
......@@ -82,17 +138,48 @@ impl InterferenceGraph {
matrix[(from.0, to.0)] || matrix[(to.0, from.0)]
}
pub fn outdegree_of(&self, node: Node) -> usize {
let mut count = 0;
for i in 0..self.nodes.len() {
if self.matrix.as_ref().unwrap()[(node.0, i)] {
count += 1;
}
}
count
}
pub fn indegree_of(&self, node: Node) -> usize {
let mut count = 0;
for i in 0..self.nodes.len() {
if self.matrix.as_ref().unwrap()[(i, node.0)] {
count += 1;
}
}
count
}
pub fn degree_of(&self, node: Node) -> usize {
self.outdegree_of(node) + self.indegree_of(node)
}
pub fn print(&self) {
println!("");
println!("Interference Graph");
println!("nodes:");
for id in self.nodes.keys() {
println!("Reg {} -> {:?}", id, self.nodes.get(&id).unwrap());
}
println!("color:");
for (n, c) in self.color.iter() {
println!("Node {} -> Color/Reg {}", n.0, c);
for (n, c) in self.nodes_property.iter() {
println!("{:?} -> Color/Reg {:?}", n, c);
}
println!("moves:");
for mov in self.moves.iter() {
println!("Move {} -> {}", mov.0, mov.1);
println!("Move {:?} -> {:?}", mov.from, mov.to);
}
println!("graph:");
{
......@@ -120,51 +207,9 @@ impl InterferenceGraph {
}
println!("");
}
#[allow(dead_code)]
pub fn print_symbols(&self, func: &MuFunction) {
let ref context = func.context;
println!("");
println!("Interference Graph");
println!("color:");
for (n, c) in self.color.iter() {
println!("Node {} -> Color/Reg {}", get_tag(n.0, context), get_tag(*c, context));
}
println!("moves:");
for mov in self.moves.iter() {
println!("Move {} -> {}", get_tag(mov.0, context), get_tag(mov.1, context));
}
println!("graph:");
{
let idx_to_node_id = {
let mut ret : HashMap<Node, MuID> = HashMap::new();
for node_id in self.nodes.keys() {
ret.insert(*self.nodes.get(node_id).unwrap(), *node_id);
}
ret
};
let matrix = self.matrix.as_ref().unwrap();
for i in 0..matrix.ncols() {
for j in 0..matrix.nrows() {
if matrix[(i, j)] {
let from_node = idx_to_node_id.get(&Node(i)).unwrap();
let to_node = idx_to_node_id.get(&Node(j)).unwrap();
println!("Reg {} -> Reg {}", get_tag(*from_node, context), get_tag(*to_node, context));
}
}
}
}
println!("");
}
}
fn is_machine_reg(reg: MuID) -> bool {
pub fn is_machine_reg(reg: MuID) -> bool {
if reg < RESERVED_NODE_IDS_FOR_MACHINE {
true
} else {
......@@ -173,10 +218,15 @@ fn is_machine_reg(reg: MuID) -> bool {
}
// from tony's code src/RegAlloc/Liveness.java
pub fn build (cf: &CompiledFunction) -> InterferenceGraph {
pub fn build (cf: &CompiledFunction, func: &MuFunction) -> InterferenceGraph {
let mut ig = InterferenceGraph::new();
// move precolor nodes to later iteration of registers
// precolor machine register nodes
for reg in backend::all_regs().iter() {
let reg_id = reg.extract_ssa_id().unwrap();
let node = ig.new_node(reg_id, &func.context);
ig.color_node(node, reg_id);
}
// Liveness Analysis
let n_insts = cf.mc.number_of_insts();
......@@ -191,24 +241,14 @@ pub fn build (cf: &CompiledFunction) -> InterferenceGraph {
for reg_id in cf.mc.get_inst_reg_defines(i) {
let reg_id = *reg_id;
let node = ig.new_node(reg_id);
// precolor
if is_machine_reg(reg_id) {
ig.color_node(node, reg_id);
}
ig.new_node(reg_id, &func.context);
}
for reg_id in cf.mc.get_inst_reg_uses(i) {
let reg_id = *reg_id;
let node = ig.new_node(reg_id);
ig.new_node(reg_id, &func.context);
in_set.push(reg_id);
// precolor
if is_machine_reg(reg_id) {
ig.color_node(node, reg_id);
}
}
work_list.push_front(i);
......@@ -274,7 +314,9 @@ pub fn build (cf: &CompiledFunction) -> InterferenceGraph {
debug_assert!(dst.len() == 1);
if src.len() == 1 {
ig.add_move(src[0], dst[0]);
let node1 = ig.get_node(src[0]);
let node2 = ig.get_node(dst[0]);
ig.add_move(node1, node2);
Some(src[0])
} else {
......@@ -292,10 +334,10 @@ pub fn build (cf: &CompiledFunction) -> InterferenceGraph {
let to = ig.get_node(*t);
if !ig.is_same_node(from, to) && !ig.is_adj(from, to) {
if !ig.node_has_color(from) {
if !ig.is_colored(from) {
ig.add_interference_edge(from, to);
}
if !ig.node_has_color(to) {
if !ig.is_colored(to) {
ig.add_interference_edge(to, from);
}
}
......@@ -315,8 +357,6 @@ pub fn build (cf: &CompiledFunction) -> InterferenceGraph {
ig
}
use std::fmt;
fn add_all<T: Copy + PartialEq> (vec: &mut Vec<T>, vec2: &Vec<T>) -> bool {
let mut is_changed = false;
......@@ -330,7 +370,7 @@ fn add_all<T: Copy + PartialEq> (vec: &mut Vec<T>, vec2: &Vec<T>) -> bool {
is_changed
}
fn find_value<T: Ord + fmt::Debug + fmt::Display> (vec: &mut Vec<T>, val: T) -> Option<usize> {
pub fn find_value<T: PartialEq> (vec: &Vec<T>, val: T) -> Option<usize> {
for i in 0..vec.len() {
if vec[i] == val {
return Some(i);
......@@ -340,7 +380,7 @@ fn find_value<T: Ord + fmt::Debug + fmt::Display> (vec: &mut Vec<T>, val: T) ->
None
}
fn remove_value<T: Ord + fmt::Debug + fmt::Display> (vec: &mut Vec<T>, val: T) {
fn remove_value<T: PartialEq> (vec: &mut Vec<T>, val: T) {
match find_value(vec, val) {
Some(index) => {vec.remove(index);},
None => {} // do nothing
......
#![allow(dead_code)]
use compiler::CompilerPass;
use ast::ir::*;
use vm::context::VMContext;
use compiler::backend::init_machine_regs_for_func;
mod liveness;
mod coloring;
......@@ -29,7 +33,10 @@ impl CompilerPass for RegisterAllocation {
cf.mc.print();
let liveness = liveness::build(&mut cf);
// initialize machine registers for the function context
init_machine_regs_for_func(&mut func.context);
let liveness = liveness::build(&mut cf, func);
liveness.print();
coloring::GraphColoring::start(&mut cf, liveness);
......
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