Commit 9ebbb02c authored by qinsoon's avatar qinsoon

using IR level liveness info in register allocation

parent 59365600
target/* target/*
emit/* emit/*
Cargo.lock Cargo.lock
*.log
...@@ -15,6 +15,7 @@ use std::collections::HashMap; ...@@ -15,6 +15,7 @@ use std::collections::HashMap;
use std::str; use std::str;
use std::usize; use std::usize;
use std::slice::Iter; use std::slice::Iter;
use std::ops;
struct ASMCode { struct ASMCode {
name: MuTag, name: MuTag,
...@@ -30,6 +31,10 @@ struct ASMCode { ...@@ -30,6 +31,10 @@ struct ASMCode {
cond_branches: HashMap<usize, MuTag>, cond_branches: HashMap<usize, MuTag>,
branches: HashMap<usize, MuTag>, branches: HashMap<usize, MuTag>,
blocks: Vec<MuTag>,
block_start: HashMap<MuTag, usize>,
block_range: HashMap<MuTag, ops::Range<usize>>,
block_livein: HashMap<MuTag, Vec<MuID>>, block_livein: HashMap<MuTag, Vec<MuID>>,
block_liveout: HashMap<MuTag, Vec<MuID>> block_liveout: HashMap<MuTag, Vec<MuID>>
} }
...@@ -117,6 +122,25 @@ impl MachineCode for ASMCode { ...@@ -117,6 +122,25 @@ impl MachineCode for ASMCode {
println!(""); println!("");
} }
fn get_ir_block_livein(&self, block: MuTag) -> Option<&Vec<MuID>> {
self.block_livein.get(&block)
}
fn get_ir_block_liveout(&self, block: MuTag) -> Option<&Vec<MuID>> {
self.block_liveout.get(&block)
}
fn get_all_blocks(&self) -> &Vec<MuTag> {
&self.blocks
}
fn get_block_range(&self, block: MuTag) -> Option<ops::Range<usize>> {
match self.block_range.get(&block) {
Some(r) => Some(r.clone()),
None => None
}
}
} }
struct ASM { struct ASM {
...@@ -426,6 +450,10 @@ impl CodeGenerator for ASMCodeGen { ...@@ -426,6 +450,10 @@ impl CodeGenerator for ASMCodeGen {
cond_branches: HashMap::new(), cond_branches: HashMap::new(),
branches: HashMap::new(), branches: HashMap::new(),
blocks: vec![],
block_start: HashMap::new(),
block_range: HashMap::new(),
block_livein: HashMap::new(), block_livein: HashMap::new(),
block_liveout: HashMap::new() block_liveout: HashMap::new()
})); }));
...@@ -464,6 +492,17 @@ impl CodeGenerator for ASMCodeGen { ...@@ -464,6 +492,17 @@ impl CodeGenerator for ASMCodeGen {
fn start_block(&mut self, block_name: MuTag) { fn start_block(&mut self, block_name: MuTag) {
let label = format!("{}:", self.asm_block_label(block_name)); let label = format!("{}:", self.asm_block_label(block_name));
self.add_asm_block_label(label, block_name); self.add_asm_block_label(label, block_name);
self.cur_mut().blocks.push(block_name);
let start = self.line();
self.cur_mut().block_start.insert(block_name, start);
}
fn end_block(&mut self, block_name: MuTag) {
let start : usize = *self.cur().block_start.get(&block_name).unwrap();
let end : usize = self.line();
self.cur_mut().block_range.insert(block_name, (start..end));
} }
fn set_block_livein(&mut self, block_name: MuTag, live_in: &Vec<P<Value>>) { fn set_block_livein(&mut self, block_name: MuTag, live_in: &Vec<P<Value>>) {
......
...@@ -13,6 +13,7 @@ pub trait CodeGenerator { ...@@ -13,6 +13,7 @@ pub trait CodeGenerator {
fn start_block(&mut self, block_name: MuTag); fn start_block(&mut self, block_name: MuTag);
fn set_block_livein(&mut self, block_name: MuTag, live_in: &Vec<P<Value>>); fn set_block_livein(&mut self, block_name: MuTag, live_in: &Vec<P<Value>>);
fn set_block_liveout(&mut self, block_name: MuTag, live_out: &Vec<P<Value>>); fn set_block_liveout(&mut self, block_name: MuTag, live_out: &Vec<P<Value>>);
fn end_block(&mut self, block_name: MuTag);
fn emit_cmp_r64_r64(&mut self, op1: &P<Value>, op2: &P<Value>); fn emit_cmp_r64_r64(&mut self, op1: &P<Value>, op2: &P<Value>);
fn emit_cmp_r64_imm32(&mut self, op1: &P<Value>, op2: u32); fn emit_cmp_r64_imm32(&mut self, op1: &P<Value>, op2: u32);
......
...@@ -380,7 +380,13 @@ impl <'a> InstructionSelection { ...@@ -380,7 +380,13 @@ impl <'a> InstructionSelection {
} }
fn emit_common_prologue(&mut self, args: &Vec<P<Value>>) { fn emit_common_prologue(&mut self, args: &Vec<P<Value>>) {
self.backend.start_block("prologue"); let block_name = "prologue";
self.backend.start_block(block_name);
// no livein
// liveout = entry block's args
self.backend.set_block_livein(block_name, &vec![]);
self.backend.set_block_liveout(block_name, args);
// push rbp // push rbp
self.backend.emit_push_r64(&x86_64::RBP); self.backend.emit_push_r64(&x86_64::RBP);
...@@ -414,10 +420,13 @@ impl <'a> InstructionSelection { ...@@ -414,10 +420,13 @@ impl <'a> InstructionSelection {
panic!("expect an arg value to be either int reg or fp reg"); panic!("expect an arg value to be either int reg or fp reg");
} }
} }
self.backend.end_block(block_name);
} }
fn emit_common_epilogue(&mut self, ret_inst: &Instruction, cur_func: &MuFunction) { fn emit_common_epilogue(&mut self, ret_inst: &Instruction, cur_func: &MuFunction) {
self.backend.start_block("epilogue"); // epilogue is not a block (its a few instruction inserted before return)
// FIXME: this may change in the future
// prepare return regs // prepare return regs
let ref ops = ret_inst.ops.borrow(); let ref ops = ret_inst.ops.borrow();
...@@ -692,6 +701,8 @@ impl CompilerPass for InstructionSelection { ...@@ -692,6 +701,8 @@ impl CompilerPass for InstructionSelection {
for inst in block_content.body.iter() { for inst in block_content.body.iter() {
self.instruction_select(inst, func); self.instruction_select(inst, func);
} }
self.backend.end_block(block.label);
} }
} }
......
...@@ -6,6 +6,7 @@ use ast::ir::*; ...@@ -6,6 +6,7 @@ use ast::ir::*;
use ast::types; use ast::types;
use compiler::backend; use compiler::backend;
use utils::vec_utils; use utils::vec_utils;
use utils::LinkedHashSet;
use std::collections::LinkedList; use std::collections::LinkedList;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
...@@ -260,7 +261,111 @@ pub fn is_machine_reg(reg: MuID) -> bool { ...@@ -260,7 +261,111 @@ pub fn is_machine_reg(reg: MuID) -> bool {
} }
} }
// from Tailoring Graph-coloring Register Allocation For Runtime Compilation, Figure 4
pub fn build_chaitin_briggs (cf: &CompiledFunction, func: &MuFunction) -> InterferenceGraph {
let mut ig = InterferenceGraph::new();
// 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);
}
// Initialize and creates nodes for all the involved temps/regs
for i in 0..cf.mc.number_of_insts() {
for reg_id in cf.mc.get_inst_reg_defines(i) {
let reg_id = *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;
ig.new_node(reg_id, &func.context);
}
}
// all nodes has been added, we init graph (create adjacency matrix)
ig.init_graph();
for block in cf.mc.get_all_blocks() {
// Current_Live(B) = LiveOut(B)
let mut current_live = LinkedHashSet::from_vec(match cf.mc.get_ir_block_liveout(block) {
Some(liveout) => liveout.to_vec(),
None => panic!("cannot find liveout for block {}", block)
});
let range = cf.mc.get_block_range(block);
if range.is_none() {
continue;
}
// for every inst I in reverse order
for i in range.unwrap().rev() {
let src : Option<MuID> = {
if cf.mc.is_move(i) {
let src = cf.mc.get_inst_reg_uses(i);
let dst = cf.mc.get_inst_reg_defines(i);
// src may be an immediate number
// but dest is definitly a register
debug_assert!(dst.len() == 1);
if src.len() == 1 {
let node1 = ig.get_node(src[0]);
let node2 = ig.get_node(dst[0]);
ig.add_move(node1, node2);
Some(src[0])
} else {
None
}
} else {
None
}
};
// for every definition D in I
for d in cf.mc.get_inst_reg_defines(i) {
// add an interference from D to every element E in Current_Live - {D}
// creating nodes if necessary
for e in current_live.iter() {
if src.is_none() || (src.is_some() && *e != src.unwrap()) {
let from = ig.get_node(*d);
let to = ig.get_node(*e);
if !ig.is_same_node(from, to) && !ig.is_adj(from, to) {
if !ig.is_colored(from) {
ig.add_interference_edge(from, to);
}
if !ig.is_colored(to) {
ig.add_interference_edge(to, from);
}
}
}
}
}
// for every definition D in I
for d in cf.mc.get_inst_reg_defines(i) {
// remove D from Current_Live
current_live.remove(d);
}
// for every use U in I
for u in cf.mc.get_inst_reg_uses(i) {
// add U to Current_live
current_live.insert(*u);
}
}
}
ig
}
// from tony's code src/RegAlloc/Liveness.java // from tony's code src/RegAlloc/Liveness.java
// this function is no longer used
#[allow(dead_code)]
pub fn build (cf: &CompiledFunction, func: &MuFunction) -> InterferenceGraph { pub fn build (cf: &CompiledFunction, func: &MuFunction) -> InterferenceGraph {
let mut ig = InterferenceGraph::new(); let mut ig = InterferenceGraph::new();
......
...@@ -2,5 +2,6 @@ mod liveness; ...@@ -2,5 +2,6 @@ mod liveness;
mod coloring; mod coloring;
pub use compiler::backend::reg_alloc::graph_coloring::liveness::InterferenceGraph; 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 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; pub use compiler::backend::reg_alloc::graph_coloring::coloring::GraphColoring;
\ No newline at end of file
...@@ -4,7 +4,7 @@ extern crate lazy_static; ...@@ -4,7 +4,7 @@ extern crate lazy_static;
extern crate log; extern crate log;
#[macro_use] #[macro_use]
mod utils; pub mod utils;
pub mod ast; pub mod ast;
pub mod vm; pub mod vm;
pub mod compiler; pub mod compiler;
...@@ -13,6 +13,16 @@ impl<K: Hash + Eq> LinkedHashSet<K> { ...@@ -13,6 +13,16 @@ impl<K: Hash + Eq> LinkedHashSet<K> {
pub fn new() -> Self { pub fn new() -> Self {
LinkedHashSet(LinkedHashMap::new()) LinkedHashSet(LinkedHashMap::new())
} }
pub fn from_vec(from: Vec<K>) -> Self {
let mut ret = LinkedHashSet::new();
for ele in from {
ret.insert(ele);
}
ret
}
} }
impl<K: Hash + Eq, S: BuildHasher> LinkedHashSet<K, S> { impl<K: Hash + Eq, S: BuildHasher> LinkedHashSet<K, S> {
......
...@@ -20,6 +20,18 @@ macro_rules! select_value { ...@@ -20,6 +20,18 @@ macro_rules! select_value {
pub mod vec_utils { pub mod vec_utils {
use std::fmt; use std::fmt;
pub fn is_identical_to_str_ignore_order<T: Ord + fmt::Display + Clone, Q: Ord + fmt::Display + Clone> (vec: &Vec<T>, mut expect: Vec<Q>) -> bool {
let mut vec_copy = vec.to_vec();
vec_copy.sort();
expect.sort();
let a = as_str(&vec_copy);
let b = as_str(&expect);
a == b
}
pub fn as_str<T: fmt::Display>(vec: &Vec<T>) -> String { pub fn as_str<T: fmt::Display>(vec: &Vec<T>) -> String {
let mut ret = String::new(); let mut ret = String::new();
for i in 0..vec.len() { for i in 0..vec.len() {
......
use ast::ir::*; use ast::ir::*;
use std::ops;
pub struct CompiledFunction { pub struct CompiledFunction {
pub fn_name: MuTag, pub fn_name: MuTag,
...@@ -19,4 +20,10 @@ pub trait MachineCode { ...@@ -19,4 +20,10 @@ pub trait MachineCode {
fn get_inst_reg_defines(&self, index: usize) -> &Vec<MuID>; fn get_inst_reg_defines(&self, index: usize) -> &Vec<MuID>;
fn replace_reg(&mut self, from: MuID, to: MuID); fn replace_reg(&mut self, from: MuID, to: MuID);
fn get_ir_block_livein(&self, block: MuTag) -> Option<&Vec<MuID>>;
fn get_ir_block_liveout(&self, block: MuTag) -> Option<&Vec<MuID>>;
fn get_all_blocks(&self) -> &Vec<MuTag>;
fn get_block_range(&self, block: MuTag) -> Option<ops::Range<usize>>;
} }
\ No newline at end of file
...@@ -4,9 +4,63 @@ extern crate simple_logger; ...@@ -4,9 +4,63 @@ extern crate simple_logger;
use test_ir::test_ir::factorial; use test_ir::test_ir::factorial;
use self::mu::compiler::*; use self::mu::compiler::*;
use self::mu::utils::vec_utils;
use self::mu::ast::ir::*;
use std::sync::Arc; use std::sync::Arc;
#[test]
fn test_ir_liveness_fac() {
simple_logger::init_with_level(log::LogLevel::Trace).ok();
let vm_context = Arc::new(factorial());
let compiler = Compiler::new(CompilerPolicy::new(vec![
Box::new(passes::DefUse::new()),
Box::new(passes::TreeGen::new()),
Box::new(passes::ControlFlowAnalysis::new()),
Box::new(passes::TraceGen::new()),
Box::new(backend::inst_sel::InstructionSelection::new()),
]), vm_context.clone());
let funcs = vm_context.funcs().read().unwrap();
let mut factorial_func = funcs.get("fac").unwrap().borrow_mut();
compiler.compile(&mut factorial_func);
let cf_lock = vm_context.compiled_funcs().read().unwrap();
let cf = cf_lock.get("fac").unwrap().borrow();
// block 0
let block_0_livein = cf.mc.get_ir_block_livein("blk_0").unwrap();
let blk_0_n_3 = factorial_func.context.get_value_by_tag("blk_0_n_3").unwrap().id;
assert!(vec_utils::is_identical_to_str_ignore_order(block_0_livein, vec![blk_0_n_3]));
let block_0_liveout = cf.mc.get_ir_block_liveout("blk_0").unwrap();
assert!(vec_utils::is_identical_to_str_ignore_order(block_0_liveout, vec![blk_0_n_3]));
// block 1
let block_1_livein = cf.mc.get_ir_block_livein("blk_1").unwrap();
let blk_1_n_3 = factorial_func.context.get_value_by_tag("blk_1_n_3").unwrap().id;
assert!(vec_utils::is_identical_to_str_ignore_order(block_1_livein, vec![blk_1_n_3]));
let block_1_liveout = cf.mc.get_ir_block_liveout("blk_1").unwrap();
let blk_1_v52 = factorial_func.context.get_value_by_tag("blk_1_v52").unwrap().id;
assert!(vec_utils::is_identical_to_str_ignore_order(block_1_liveout, vec![blk_1_v52]));
// block 2
let block_2_livein = cf.mc.get_ir_block_livein("blk_2").unwrap();
let blk_2_v53 = factorial_func.context.get_value_by_tag("blk_2_v53").unwrap().id;
assert!(vec_utils::is_identical_to_str_ignore_order(block_2_livein, vec![blk_2_v53]));
let block_2_liveout = cf.mc.get_ir_block_liveout("blk_2").unwrap();
let expect : Vec<MuID> = vec![];
assert!(vec_utils::is_identical_to_str_ignore_order(block_2_liveout, expect));
}
#[test] #[test]
fn test_regalloc_fac() { fn test_regalloc_fac() {
simple_logger::init_with_level(log::LogLevel::Trace).ok(); simple_logger::init_with_level(log::LogLevel::Trace).ok();
......
...@@ -275,6 +275,7 @@ pub fn factorial() -> VMContext { ...@@ -275,6 +275,7 @@ pub fn factorial() -> VMContext {
v: Instruction_::BinOp(BinOp::Mul, 0, 1) v: Instruction_::BinOp(BinOp::Mul, 0, 1)
}); });
// BRANCH blk_2 (%blk_1_v52)
let blk_1_term = func.new_inst(Instruction{ let blk_1_term = func.new_inst(Instruction{
value: None, value: None,
ops: RefCell::new(vec![blk_1_v52.clone()]), ops: RefCell::new(vec![blk_1_v52.clone()]),
......
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