Commit 592b797b authored by John Zhang's avatar John Zhang
Browse files

Merge branch 'master' of gitlab.anu.edu.au:mu/mu-impl-fast

parents 0d36d6ca fdec1e4f
...@@ -74,7 +74,7 @@ impl ASMCode { ...@@ -74,7 +74,7 @@ impl ASMCode {
false false
} }
fn is_block_end(&self, inst: usize) -> bool { fn is_last_inst_in_block(&self, inst: usize) -> bool {
for block in self.blocks.values() { for block in self.blocks.values() {
if block.end_inst == inst + 1 { if block.end_inst == inst + 1 {
return true; return true;
...@@ -108,6 +108,7 @@ impl ASMCode { ...@@ -108,6 +108,7 @@ impl ASMCode {
insert_before: HashMap<usize, Vec<Box<ASMCode>>>, insert_before: HashMap<usize, Vec<Box<ASMCode>>>,
insert_after: HashMap<usize, Vec<Box<ASMCode>>>) -> Box<ASMCode> insert_after: HashMap<usize, Vec<Box<ASMCode>>>) -> Box<ASMCode>
{ {
trace!("insert spilling code");
let mut ret = ASMCode { let mut ret = ASMCode {
name: self.name.clone(), name: self.name.clone(),
code: vec![], code: vec![],
...@@ -124,8 +125,11 @@ impl ASMCode { ...@@ -124,8 +125,11 @@ impl ASMCode {
let mut location_map : HashMap<usize, usize> = HashMap::new(); let mut location_map : HashMap<usize, usize> = HashMap::new();
for i in 0..self.number_of_insts() { for i in 0..self.number_of_insts() {
trace!("Inst{}", i);
if self.is_block_start(i) { if self.is_block_start(i) {
cur_block_start = i + inst_offset; cur_block_start = i + inst_offset;
trace!(" block start is shifted to {}", cur_block_start);
} }
// insert code before this instruction // insert code before this instruction
...@@ -133,6 +137,7 @@ impl ASMCode { ...@@ -133,6 +137,7 @@ impl ASMCode {
for insert in insert_before.get(&i).unwrap() { for insert in insert_before.get(&i).unwrap() {
ret.append_code_sequence_all(insert); ret.append_code_sequence_all(insert);
inst_offset += insert.number_of_insts(); inst_offset += insert.number_of_insts();
trace!(" inserted {} insts before", insert.number_of_insts());
} }
} }
...@@ -141,6 +146,7 @@ impl ASMCode { ...@@ -141,6 +146,7 @@ impl ASMCode {
// old ith inst is now the (i + inst_offset)th instruction // old ith inst is now the (i + inst_offset)th instruction
location_map.insert(i, i + inst_offset); location_map.insert(i, i + inst_offset);
trace!(" Inst{} is now Inst{}", i, i + inst_offset);
// this instruction has been offset by several instructions('inst_offset') // this instruction has been offset by several instructions('inst_offset')
// update its info // update its info
...@@ -169,19 +175,28 @@ impl ASMCode { ...@@ -169,19 +175,28 @@ impl ASMCode {
for insert in insert_after.get(&i).unwrap() { for insert in insert_after.get(&i).unwrap() {
ret.append_code_sequence_all(insert); ret.append_code_sequence_all(insert);
inst_offset += insert.number_of_insts(); inst_offset += insert.number_of_insts();
trace!(" inserted {} insts after", insert.number_of_insts());
} }
} }
if self.is_block_end(i) { if self.is_last_inst_in_block(i) {
let cur_block_end = i + inst_offset; let cur_block_end = i + 1 + inst_offset;
// copy the block // copy the block
let (name, block) = self.get_block_by_inst(i); let (name, block) = self.get_block_by_inst(i);
let mut new_block = block.clone(); let mut new_block = ASMBlock{
new_block.start_inst = cur_block_start; start_inst: cur_block_start,
end_inst: cur_block_end,
livein: vec![],
liveout: vec![]
};
trace!(" old block: {:?}", block);
trace!(" new block: {:?}", new_block);
cur_block_start = usize::MAX; cur_block_start = usize::MAX;
new_block.end_inst = cur_block_end;
// add to the new code // add to the new code
ret.blocks.insert(name.clone(), new_block); ret.blocks.insert(name.clone(), new_block);
...@@ -780,18 +795,11 @@ impl ASMCodeGen { ...@@ -780,18 +795,11 @@ impl ASMCodeGen {
} }
fn add_asm_ret(&mut self, code: String) { fn add_asm_ret(&mut self, code: String) {
let uses : HashMap<MuID, Vec<ASMLocation>> = { // return instruction does not use anything (not RETURN REGS)
let mut ret = HashMap::new(); // otherwise it will keep RETURN REGS alive
for reg in x86_64::RETURN_GPRs.iter() { // and if there is no actual move into RETURN REGS, it will keep RETURN REGS for alive for very long
ret.insert(reg.id(), vec![]); // and prevents anything using those regsiters
} self.add_asm_inst(code, hashmap!{}, hashmap!{}, false);
for reg in x86_64::RETURN_FPRs.iter() {
ret.insert(reg.id(), vec![]);
}
ret
};
self.add_asm_inst(code, hashmap!{}, uses, false);
} }
fn add_asm_branch(&mut self, code: String, target: MuName) { fn add_asm_branch(&mut self, code: String, target: MuName) {
......
...@@ -390,6 +390,8 @@ impl <'a> GraphColoring<'a> { ...@@ -390,6 +390,8 @@ impl <'a> GraphColoring<'a> {
self.add_worklist(u); self.add_worklist(u);
} }
} else if precolored_v || self.ig.is_adj(u, v) { } else if precolored_v || self.ig.is_adj(u, v) {
trace!("precolored_v: {}", precolored_v);
trace!("is_adj(u, v): {}", self.ig.is_adj(u, v));
trace!("v is precolored or u,v is adjacent, the move is constrained"); trace!("v is precolored or u,v is adjacent, the move is constrained");
self.constrained_moves.insert(m); self.constrained_moves.insert(m);
if !precolored_u { if !precolored_u {
...@@ -430,11 +432,11 @@ impl <'a> GraphColoring<'a> { ...@@ -430,11 +432,11 @@ impl <'a> GraphColoring<'a> {
fn ok(&self, u: NodeIndex, v: NodeIndex) -> bool { fn ok(&self, u: NodeIndex, v: NodeIndex) -> bool {
for t in self.adjacent(v).iter() { for t in self.adjacent(v).iter() {
let t = *t; let t = *t;
if !self.precolored.contains(&t) if !(self.degree(t) < self.n_regs_for_node(t)
|| self.degree(t) < self.n_regs_for_node(t) || self.precolored.contains(&t)
|| self.ig.is_adj(t, u) { || self.ig.is_adj(t, u)) {
return false; return false;
} }
} }
true true
...@@ -453,7 +455,8 @@ impl <'a> GraphColoring<'a> { ...@@ -453,7 +455,8 @@ impl <'a> GraphColoring<'a> {
let mut k = 0; let mut k = 0;
for n in nodes.iter() { for n in nodes.iter() {
if self.precolored.contains(n) || self.degree(*n) >= self.n_regs_for_node(*n) { // if self.precolored.contains(n) || self.degree(*n) >= self.n_regs_for_node(*n) {
if self.degree(*n) >= self.n_regs_for_node(*n) {
k += 1; k += 1;
} }
} }
......
use compiler::machine_code::CompiledFunction; use compiler::machine_code::CompiledFunction;
use ast::ir::*; use ast::ir::*;
use compiler::backend; use compiler::backend;
use utils::vec_utils;
use utils::LinkedHashSet; use utils::LinkedHashSet;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
...@@ -101,7 +100,12 @@ impl InterferenceGraph { ...@@ -101,7 +100,12 @@ impl InterferenceGraph {
} }
pub fn is_interferenced_with(&self, node1: NodeIndex, node2: NodeIndex) -> bool { pub fn is_interferenced_with(&self, node1: NodeIndex, node2: NodeIndex) -> bool {
self.graph.find_edge(node1, node2).is_some() trace!("trying to find edge between {:?} and {:?}", node1, node2);
let edge = self.graph.find_edge(node1, node2);
trace!("edge: {:?}", edge);
edge.is_some()
} }
pub fn color_node(&mut self, node: NodeIndex, color: MuID) { pub fn color_node(&mut self, node: NodeIndex, color: MuID) {
...@@ -160,6 +164,9 @@ impl InterferenceGraph { ...@@ -160,6 +164,9 @@ impl InterferenceGraph {
} }
pub fn print(&self, context: &FunctionContext) { pub fn print(&self, context: &FunctionContext) {
use compiler::backend::reg_alloc::graph_coloring::petgraph::dot::Dot;
use compiler::backend::reg_alloc::graph_coloring::petgraph::dot::Config;
debug!(""); debug!("");
debug!("Interference Graph"); debug!("Interference Graph");
...@@ -175,72 +182,74 @@ impl InterferenceGraph { ...@@ -175,72 +182,74 @@ impl InterferenceGraph {
} }
debug!("graph:"); debug!("graph:");
debug!("{:?}", self.graph); debug!("\n\n{:?}\n", Dot::with_config(&self.graph, &[Config::EdgeNoLabel]));
debug!(""); debug!("");
} }
} }
#[allow(unused_variables)] fn build_live_set (cf: &mut CompiledFunction) {
fn build_live_set(cf: &mut CompiledFunction, func: &MuFunctionVersion) { info!("start building live set");
let n_insts = cf.mc().number_of_insts(); let n_insts = cf.mc().number_of_insts();
let mut livein : Vec<Vec<MuID>> = vec![vec![]; n_insts]; let mut livein : Vec<LinkedHashSet<MuID>> = vec![LinkedHashSet::new(); n_insts];
let mut liveout : Vec<Vec<MuID>> = vec![vec![]; n_insts]; let mut liveout : Vec<LinkedHashSet<MuID>> = vec![LinkedHashSet::new(); n_insts];
let mut is_changed = true; let mut is_changed = true;
while is_changed { while is_changed {
// reset // reset
is_changed = false; is_changed = false;
for n in 0..n_insts { for n in 0..n_insts {
let in_set_old = livein[n].to_vec(); // copy to new vec let in_set_old = livein[n].clone();
let out_set_old = liveout[n].to_vec(); let out_set_old = liveout[n].clone();
// in[n] <- use[n] + (out[n] - def[n]) // in[n] <- use[n] + (out[n] - def[n]);
// (1) in[n] = use[n] {
let mut in_set_new = vec![]; let ref mut inset = livein[n];
in_set_new.extend_from_slice(&cf.mc().get_inst_reg_uses(n));
// (2) diff = out[n] - def[n] inset.clear();
let mut diff = liveout[n].to_vec();
for def in cf.mc().get_inst_reg_defines(n) { // (1) in[n] = use[n]
vec_utils::remove_value(&mut diff, def); inset.add_from_vec(cf.mc().get_inst_reg_uses(n));
// (2) + out[n]
inset.add_all(liveout[n].clone());
// (3) - def[n]
for def in cf.mc().get_inst_reg_defines(n) {
inset.remove(&def);
}
} }
// (3) in[n] = in[n] + diff
vec_utils::append_unique(&mut in_set_new, &mut diff);
// update livein[n]
livein[n].clear();
livein[n].extend_from_slice(&in_set_new);
// out[n] <- union(in[s] for every successor s of n) // out[n] <- union(in[s] for every successor s of n)
let mut union = vec![]; {
for s in cf.mc().get_succs(n) { let ref mut outset = liveout[n];
vec_utils::append_clone_unique(&mut union, &livein[*s]); outset.clear();
for s in cf.mc().get_succs(n) {
outset.add_all(livein[*s].clone());
}
} }
// update liveout[n] // is in/out changed in this iteration?
liveout[n].clear(); let n_changed = !in_set_old.equals(&livein[n]) || !out_set_old.equals(&liveout[n]);
liveout[n].extend_from_slice(&union);
let n_changed = !vec_utils::is_identical_ignore_order(&livein[n], &in_set_old)
|| !vec_utils::is_identical_ignore_order(&liveout[n], &out_set_old);
is_changed = is_changed || n_changed; is_changed = is_changed || n_changed;
} }
} }
for block in cf.mc().get_all_blocks().to_vec() { for block in cf.mc().get_all_blocks().to_vec() {
let start_inst = cf.mc().get_block_range(&block).unwrap().start; let start_inst = cf.mc().get_block_range(&block).unwrap().start;
cf.mc_mut().set_ir_block_livein(&block, livein[start_inst].to_vec()); cf.mc_mut().set_ir_block_livein(&block, livein[start_inst].clone().to_vec());
let end_inst = cf.mc().get_block_range(&block).unwrap().end; let end_inst = cf.mc().get_block_range(&block).unwrap().end;
cf.mc_mut().set_ir_block_liveout(&block, liveout[end_inst].to_vec()); cf.mc_mut().set_ir_block_liveout(&block, liveout[end_inst].clone().to_vec());
} }
} }
// from Tailoring Graph-coloring Register Allocation For Runtime Compilation, Figure 4 // from Tailoring Graph-coloring Register Allocation For Runtime Compilation, Figure 4
pub fn build_chaitin_briggs (cf: &mut CompiledFunction, func: &MuFunctionVersion) -> InterferenceGraph { pub fn build_chaitin_briggs (cf: &mut CompiledFunction, func: &MuFunctionVersion) -> InterferenceGraph {
build_live_set(cf, func); build_live_set(cf);
let mut ig = InterferenceGraph::new(); let mut ig = InterferenceGraph::new();
......
...@@ -24,12 +24,40 @@ impl<K: Hash + Eq> LinkedHashSet<K> { ...@@ -24,12 +24,40 @@ impl<K: Hash + Eq> LinkedHashSet<K> {
ret ret
} }
pub fn to_vec(mut self) -> Vec<K> {
let mut ret = vec![];
while !self.is_empty() {
ret.push(self.pop_front().unwrap());
}
ret
}
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.0.clear(); self.0.clear();
} }
} }
impl<K: Hash + Eq, S: BuildHasher> LinkedHashSet<K, S> { impl<K: Hash + Eq, S: BuildHasher> LinkedHashSet<K, S> {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn pop_front(&mut self) -> Option<K> {
match self.0.pop_front() {
Some((k, _)) => Some(k),
None => None
}
}
pub fn pop_back(&mut self) -> Option<K> {
match self.0.pop_back() {
Some((k, _)) => Some(k),
None => None
}
}
pub fn insert(&mut self, k: K) -> Option<()> { pub fn insert(&mut self, k: K) -> Option<()> {
self.0.insert(k, ()) self.0.insert(k, ())
} }
...@@ -56,19 +84,32 @@ impl<K: Hash + Eq, S: BuildHasher> LinkedHashSet<K, S> { ...@@ -56,19 +84,32 @@ impl<K: Hash + Eq, S: BuildHasher> LinkedHashSet<K, S> {
self.0.is_empty() self.0.is_empty()
} }
pub fn pop_front(&mut self) -> Option<K> {
match self.0.pop_front() {
Some((k, _)) => Some(k),
None => None
}
}
pub fn add_all(&mut self, mut other: Self) { pub fn add_all(&mut self, mut other: Self) {
while !other.is_empty() { while !other.is_empty() {
let entry = other.pop_front().unwrap(); let entry = other.pop_front().unwrap();
self.insert(entry); self.insert(entry);
} }
} }
pub fn add_from_vec(&mut self, mut vec: Vec<K>) {
while !vec.is_empty() {
self.insert(vec.pop().unwrap());
}
}
pub fn equals(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
for ele in self.iter() {
if !other.contains(ele) {
return false;
}
}
true
}
} }
impl<K: Hash + Eq + Clone> Clone for LinkedHashSet<K> { impl<K: Hash + Eq + Clone> Clone for LinkedHashSet<K> {
......
...@@ -260,7 +260,7 @@ macro_rules! inst { ...@@ -260,7 +260,7 @@ macro_rules! inst {
}; };
// RET // RET
(($vm: expr, $fv: ident) $name: ident: RET ($($val: ident), *)) => { (($vm: expr, $fv: ident) $name: ident: RET ($($val: ident), +)) => {
let $name = $fv.new_inst(Instruction{ let $name = $fv.new_inst(Instruction{
hdr: MuEntityHeader::unnamed($vm.next_id()), hdr: MuEntityHeader::unnamed($vm.next_id()),
value: None, value: None,
...@@ -271,4 +271,13 @@ macro_rules! inst { ...@@ -271,4 +271,13 @@ macro_rules! inst {
}) })
}); });
}; };
// RET (no value)
(($vm: expr, $fv: ident) $name: ident: RET) => {
let $name = $fv.new_inst(Instruction{
hdr: MuEntityHeader::unnamed($vm.next_id()),
value: None,
ops: RwLock::new(vec![]),
v: Instruction_::Return(vec![])
});
};
} }
\ No newline at end of file
...@@ -564,5 +564,85 @@ fn create_simple_spill() -> VM { ...@@ -564,5 +564,85 @@ fn create_simple_spill() -> VM {
vm.define_func_version(func_ver); vm.define_func_version(func_ver);
vm
}
#[test]
#[cfg(target_arch = "x86_64")]
fn test_coalesce_branch_moves() {
VM::start_logging_trace();
let vm = Arc::new(coalesce_branch_moves());
let compiler = Compiler::new(CompilerPolicy::default(), vm.clone());
let func_id = vm.id_of("coalesce_branch_moves");
{
let funcs = vm.funcs().read().unwrap();
let func = funcs.get(&func_id).unwrap().read().unwrap();
let func_vers = vm.func_vers().read().unwrap();
let mut func_ver = func_vers.get(&func.cur_ver.unwrap()).unwrap().write().unwrap();
compiler.compile(&mut func_ver);
// check
let fv_id = func_ver.id();
let cfs = vm.compiled_funcs().read().unwrap();
let cf = cfs.get(&fv_id).unwrap().read().unwrap();
let mut n_mov_insts = 0;
let mc = cf.mc();
for i in 0..mc.number_of_insts() {
if mc.is_move(i) {
n_mov_insts += 1;
}
}
assert!(n_mov_insts == 1, "The function should not yield any mov instructions other than mov %rsp->%rbp (some possible coalescing failed)");
}
}
fn coalesce_branch_moves() -> VM {
let vm = VM::new();
typedef! ((vm) int64 = mu_int(64));
funcsig! ((vm) sig = (int64, int64, int64, int64) -> ());
funcdecl!((vm) <sig> coalesce_branch_moves);
funcdef! ((vm) <sig> coalesce_branch_moves VERSION coalesce_branch_moves_v1);
// blk entry
block! ((vm, coalesce_branch_moves_v1) blk_entry);
ssa! ((vm, coalesce_branch_moves_v1) <int64> arg0);
ssa! ((vm, coalesce_branch_moves_v1) <int64> arg1);
ssa! ((vm, coalesce_branch_moves_v1) <int64> arg2);
ssa! ((vm, coalesce_branch_moves_v1) <int64> arg3);
block! ((vm, coalesce_branch_moves_v1) blk1);
inst! ((vm, coalesce_branch_moves_v1) blk_entry_branch:
BRANCH blk1 (arg0, arg1, arg2, arg3)
);
define_block!((vm, coalesce_branch_moves_v1) blk_entry (arg0, arg1, arg2, arg3) {blk_entry_branch});
ssa! ((vm, coalesce_branch_moves_v1) <int64> blk1_arg0);
ssa! ((vm, coalesce_branch_moves_v1) <int64> blk1_arg1);
ssa! ((vm, coalesce_branch_moves_v1) <int64> blk1_arg2);
ssa! ((vm, coalesce_branch_moves_v1) <int64> blk1_arg3);
inst! ((vm, coalesce_branch_moves_v1) blk1_ret:
RET
);
define_block!((vm, coalesce_branch_moves_v1) blk1 (blk1_arg0, blk1_arg1, blk1_arg2, blk1_arg3) {
blk1_ret
});
define_func_ver!((vm) coalesce_branch_moves_v1 (entry: blk_entry){
blk_entry, blk1
});
vm vm
} }
\ No newline at end of file