Commit 6c64ef82 authored by qinsoon's avatar qinsoon

rewrite liveness analysis. Do local LA first then do global LA.

No regression, but new tests still doesn't run. Marked them
as expect fail or skip. Going to work on them later.
parent cf103d2f
......@@ -265,7 +265,7 @@ impl ASMCode {
}
fn control_flow_analysis(&mut self) {
const TRACE_CFA : bool = false;
const TRACE_CFA : bool = true;
// control flow analysis
let n_insts = self.number_of_insts();
......@@ -276,6 +276,9 @@ impl ASMCode {
let block_start = {
let mut ret = vec![];
for block in blocks.values() {
if TRACE_CFA {
trace!("Block starts at {}", block.start_inst);
}
ret.push(block.start_inst);
}
ret
......@@ -285,19 +288,38 @@ impl ASMCode {
if TRACE_CFA {
trace!("---inst {}---", i);
}
// determine predecessor - if cur is not block start, its predecessor is previous insts
let is_block_start = block_start.contains(&i);
if !is_block_start {
if i > 0 {
if TRACE_CFA {
trace!("inst {}: not a block start", i);
trace!("inst {}: set PREDS as previous inst {}", i, i - 1);
// skip symbol
if asm[i].is_symbol {
continue;
}
// determine predecessor
// we check if it is a fallthrough block
if i != 0 {
let last_inst = ASMCode::find_prev_inst(i, asm);
match last_inst {
Some(last_inst) => {
let last_inst_branch = asm[last_inst].branch.clone();
match last_inst_branch {
// if it is a fallthrough, we set its preds as last inst
ASMBranchTarget::None => {
if !asm[i].preds.contains(&last_inst) {
asm[i].preds.push(last_inst);
if TRACE_CFA {
trace!("inst {}: set PREDS as previous inst - fallthrough {}", i, last_inst);
}
}
}
// otherwise do nothing
_ => {}
}
}
asm[i].preds.push(i - 1);
None => {}
}
} else {
// if cur is a branch target, we already set its predecessor
// if cur is a fall-through block, we set it in a sanity check pass
}
// determine successor
......@@ -324,7 +346,7 @@ impl ASMCode {
// branch to target
let target_n = self.blocks.get(target).unwrap().start_inst;
// cur insts' succ is target and next inst
// cur insts' succ is target
asm[i].succs.push(target_n);
if TRACE_CFA {
......@@ -333,18 +355,25 @@ impl ASMCode {
trace!("inst {}: set SUCCS as branch target {}", i, target_n);
}
if i < n_insts - 1 {
if TRACE_CFA {
trace!("inst {}: set SUCCS as next inst", i + 1);
}
asm[i].succs.push(i + 1);
}
// target's pred is cur
asm[target_n].preds.push(i);
if TRACE_CFA {
trace!("inst {}: set PREDS as {}", target_n, i);
}
if let Some(next_inst) = ASMCode::find_next_inst(i, asm) {
// cur succ is next inst
asm[i].succs.push(next_inst);
// next inst's pred is cur
asm[next_inst].preds.push(i);
if TRACE_CFA {
trace!("inst {}: SET SUCCS as c-branch fallthrough target {}", i, next_inst);
}
} else {
panic!("conditional branch does not have a fallthrough target");
}
},
ASMBranchTarget::Return => {
if TRACE_CFA {
......@@ -357,21 +386,52 @@ impl ASMCode {
if TRACE_CFA {
trace!("inst {}: not a branch inst", i);
}
if i < n_insts - 1 {
if let Some(next_inst) = ASMCode::find_next_inst(i, asm) {
if TRACE_CFA {
trace!("inst {}: set SUCCS as next inst {}", i, i + 1);
trace!("inst {}: set SUCCS as next inst {}", i, next_inst);
}
asm[i].succs.push(i + 1);
asm[i].succs.push(next_inst);
}
}
}
}
}
fn find_prev_inst(i: usize, asm: &Vec<ASMInst>) -> Option<usize> {
if i == 0 {
None
} else {
let mut cur = i - 1;
while cur != 0 {
if !asm[cur].is_symbol {
return Some(cur);
}
if cur == 0 {
return None;
} else {
cur -= 1;
}
}
None
}
}
// a sanity check for fallthrough blocks
for i in 0..n_insts {
if i != 0 && asm[i].preds.len() == 0 {
asm[i].preds.push(i - 1);
fn find_next_inst(i: usize, asm: &Vec<ASMInst>) -> Option<usize> {
if i >= asm.len() - 1 {
None
} else {
let mut cur = i + 1;
while cur < asm.len() {
if !asm[cur].is_symbol {
return Some(cur);
}
cur += 1;
}
None
}
}
......@@ -667,6 +727,8 @@ struct ASMInst {
uses: LinkedHashMap<MuID, Vec<ASMLocation>>,
is_mem_op_used: bool,
is_symbol: bool,
preds: Vec<usize>,
succs: Vec<usize>,
branch: ASMBranchTarget
......@@ -679,6 +741,7 @@ impl ASMInst {
defines: LinkedHashMap::new(),
uses: LinkedHashMap::new(),
is_mem_op_used: false,
is_symbol: true,
preds: vec![],
succs: vec![],
branch: ASMBranchTarget::None
......@@ -697,6 +760,7 @@ impl ASMInst {
code: inst,
defines: defines,
uses: uses,
is_symbol: false,
is_mem_op_used: is_mem_op_used,
preds: vec![],
succs: vec![],
......@@ -709,6 +773,7 @@ impl ASMInst {
code: "".to_string(),
defines: LinkedHashMap::new(),
uses: LinkedHashMap::new(),
is_symbol: false,
is_mem_op_used: false,
preds: vec![],
succs: vec![],
......
......@@ -356,7 +356,8 @@ impl <'a> InstructionSelection {
}
// jcc
_ => {
let blk_true = format!("{}_select_true", node.id());
let blk_true = format!("{}_select_true", node.id());
let blk_false = format!("{}_select_false", node.id());
let blk_end = format!("{}_select_end", node.id());
// jump to blk_true if true
......@@ -382,6 +383,14 @@ impl <'a> InstructionSelection {
_ => unimplemented!()
}
// finishing current block
let cur_block = self.current_block.as_ref().unwrap().clone();
self.backend.end_block(cur_block.clone());
// blk_false:
self.current_block = Some(blk_false.clone());
self.backend.start_block(blk_false.clone());
// mov false result here
self.emit_move_node_to_value(&tmp_res, &false_val, f_content, f_context, vm);
......@@ -505,6 +514,9 @@ impl <'a> InstructionSelection {
} else {
panic!("expecting ireg cond to be either iimm or ireg: {}", cond);
}
self.finish_block();
self.start_block(format!("{}_switch_not_met_case_{}", node.id(), case_op_index));
}
// emit default
......@@ -1729,6 +1741,9 @@ impl <'a> InstructionSelection {
self.backend.emit_cmp_imm_r(mm::LARGE_OBJECT_THRESHOLD as i32, &size);
self.backend.emit_jg(blk_alloc_large.clone());
self.finish_block();
self.start_block(format!("{}_allocsmall", node.id()));
// alloc small here
let tmp_res = self.emit_alloc_sequence_small(tmp_allocator.clone(), size.clone(), align, node, f_content, f_context, vm);
......@@ -1827,6 +1842,10 @@ impl <'a> InstructionSelection {
let slowpath = format!("{}_allocslow", node.id());
self.backend.emit_jg(slowpath.clone());
// finish current block
self.finish_block();
self.start_block(format!("{}_updatecursor", node.id()));
// update cursor
// ASM: mov %end -> [%tl + allocator_offset + cursor_offset]
self.emit_store_base_offset(&tmp_tl, cursor_offset as i32, &tmp_end, vm);
......@@ -3449,6 +3468,16 @@ impl <'a> InstructionSelection {
const_mem_val
}
}
fn finish_block(&mut self) {
let cur_block = self.current_block.as_ref().unwrap().clone();
self.backend.end_block(cur_block.clone());
}
fn start_block(&mut self, block: String) {
self.current_block = Some(block.clone());
self.backend.start_block(block);
}
}
impl CompilerPass for InstructionSelection {
......
......@@ -238,6 +238,253 @@ const TRACE_BUILD_LIVE_SET : bool = false;
fn build_live_set (cf: &mut CompiledFunction, func: &MuFunctionVersion) {
info!("---start building live set---");
let cfg = local_liveness_analysis(cf, func);
global_liveness_analysis(cfg, cf, func);
info!("---finish building live set---");
}
#[derive(Clone, Debug)]
struct CFGBlockNode {
block: String,
pred: Vec<String>,
succ: Vec<String>,
uses: Vec<MuID>,
defs: Vec<MuID>
}
fn local_liveness_analysis (cf: &mut CompiledFunction, func: &MuFunctionVersion) -> HashMap<String, CFGBlockNode> {
info!("---local liveness analysis---");
let mc = cf.mc();
let mut ret = hashmap!{};
let all_blocks = mc.get_all_blocks();
// create maps (start_inst -> name) and (end_inst -> name)
let mut start_inst_map : HashMap<usize, &str> = hashmap!{};
let mut end_inst_map : HashMap<usize, &str> = hashmap!{};
for block in all_blocks.iter() {
let range = match mc.get_block_range(block) {
Some(range) => range,
None => panic!("cannot find range for block {}", block)
};
debug!("Block {}: start_inst={}, end_inst(inclusive)={}", block, range.start, range.end-1);
start_inst_map.insert(range.start, block);
end_inst_map.insert(range.end - 1, block);
}
// local liveness analysis
for block in mc.get_all_blocks().iter() {
trace!("---block {}---", block);
let range = mc.get_block_range(block).unwrap();
let start_inst = range.start;
let end = range.end;
let mut livein = vec![];
let mut all_defined : HashSet<MuID> = HashSet::new();
for i in start_inst..end {
let reg_uses = mc.get_inst_reg_uses(i);
// if a reg is used but not defined before, it is a live-in
for reg in reg_uses {
if !all_defined.contains(&reg) {
livein.push(reg);
}
}
let reg_defs = mc.get_inst_reg_defines(i);
for reg in reg_defs {
all_defined.insert(reg);
}
}
let defs : Vec<MuID> = all_defined.into_iter().collect();
let preds : Vec<String> = {
let mut ret = vec![];
// start_inst is the first instruction
// start_inst-1 is the label for the block
// FIXME: this is confusing! label should be an attribute to an instruction
for pred in mc.get_preds(start_inst).into_iter() {
match end_inst_map.get(pred) {
Some(str) => ret.push(String::from(*str)),
None => {}
}
}
ret
};
let succs : Vec<String> = {
let mut ret = vec![];
for succ in mc.get_succs(end - 1).into_iter() {
match start_inst_map.get(succ) {
Some(str) => ret.push(String::from(*str)),
None => {}
}
}
ret
};
let node = CFGBlockNode {
block: block.clone(),
pred: preds,
succ: succs,
uses: livein,
defs: defs
};
trace!("as CFGNode {:?}", node);
ret.insert(block.clone(), node);
}
ret
}
//fn topological_sort_cfg(entry: String, cfg: HashMap<String, CFGBlockNode>) -> Vec<CFGBlockNode> {
// let mut ret = vec![];
// // for all nodes i
// // mark[i] <- false
// let mut mark = {
// let mut ret = hashmap!{};
// for str in cfg.keys() {
// ret.insert(str.clone(), false);
// }
//
// ret
// };
//
// // dfs(start-node)
// dfs(entry, &cfg, &mut mark, &mut ret);
//
// ret.reverse();
//
// ret
//}
//
//fn dfs(node: String, cfg: &HashMap<String, CFGBlockNode>, mark: &mut HashMap<String, bool>, sorted: &mut Vec<CFGBlockNode>) {
// // if mark[i] = false
// if !mark.get(&node).unwrap() {
// mark.insert(node.clone(), true);
//
// let cfg_node = cfg.get(&node).unwrap().clone();
// for succ in cfg_node.succ.iter() {
// dfs(succ.clone(), cfg, mark, sorted);
// }
//
// sorted.push(cfg_node);
// }
//}
fn global_liveness_analysis(blocks: HashMap<String, CFGBlockNode>, cf: &mut CompiledFunction, func: &MuFunctionVersion) {
let n_nodes = blocks.len();
// init live in and live out
let mut livein : HashMap<String, LinkedHashSet<MuID>> = {
let mut ret = hashmap!{};
for name in blocks.keys() {
ret.insert(name.clone(), LinkedHashSet::new());
}
ret
};
let mut liveout : HashMap<String, LinkedHashSet<MuID>> = {
let mut ret = hashmap!{};
for name in blocks.keys() {
ret.insert(name.clone(), LinkedHashSet::new());
}
ret
};
let mut is_changed = true;
let mut i = 0;
while is_changed {
trace!("---iteration {}---", i);
i += 1;
// reset
is_changed = false;
for node in blocks.keys() {
let cfg_node = blocks.get(node).unwrap();
let in_set_old = livein.get(node).unwrap().clone();
let out_set_old = liveout.get(node).unwrap().clone();
// in <- use + (out - def)
{
let mut inset = livein.get_mut(node).unwrap();
inset.clear();
// (1) out - def
inset.add_all(liveout.get(node).unwrap().clone());
for def in cfg_node.defs.iter() {
inset.remove(def);
}
// (2) in + (out - def)
for in_reg in cfg_node.uses.iter() {
inset.insert(*in_reg);
}
}
// out[n] <- union(in[s] for every successor s of n)
{
let mut outset = liveout.get_mut(node).unwrap();
outset.clear();
for s in cfg_node.succ.iter() {
outset.add_all(livein.get(s).unwrap().clone());
}
}
// is in/out changed in this iteration?
let n_changed = !in_set_old.equals(livein.get(node).unwrap()) || !out_set_old.equals(liveout.get(node).unwrap());
if TRACE_BUILD_LIVE_SET {
trace!("block {}", node);
trace!("in(old) = {:?}", in_set_old);
trace!("in(new) = {:?}", livein.get(node).unwrap());
trace!("out(old) = {:?}", out_set_old);
trace!("out(new) = {:?}", liveout.get(node).unwrap());
}
is_changed = is_changed || n_changed;
}
}
for block in blocks.keys() {
let livein : Vec<MuID> = livein.get(block).unwrap().clone().iter().map(|x| *x).collect();
{
let display_array : Vec<String> = livein.iter().map(|x| func.context.get_temp_display(*x)).collect();
trace!("livein for block {}: {:?}", block, display_array);
}
cf.mc_mut().set_ir_block_livein(block, livein);
let liveout : Vec<MuID> = liveout.get(block).unwrap().clone().iter().map(|x| *x).collect();
{
let display_array : Vec<String> = liveout.iter().map(|x| func.context.get_temp_display(*x)).collect();
trace!("liveout for block {}: {:?}", block, display_array);
}
cf.mc_mut().set_ir_block_liveout(block, liveout);
}
}
#[allow(dead_code)]
fn naive_global_liveness_analysis(cf: &mut CompiledFunction, func: &MuFunctionVersion) {
let n_insts = cf.mc().number_of_insts();
let mut livein : Vec<LinkedHashSet<MuID>> = vec![LinkedHashSet::new(); n_insts];
......@@ -296,26 +543,6 @@ fn build_live_set (cf: &mut CompiledFunction, func: &MuFunctionVersion) {
is_changed = is_changed || n_changed;
}
}
info!("---finish building live set---");
for block in cf.mc().get_all_blocks().to_vec() {
let start_inst = cf.mc().get_block_range(&block).unwrap().start;
let livein = livein[start_inst].clone().to_vec();
{
let display_array : Vec<String> = livein.iter().map(|x| func.context.get_temp_display(*x)).collect();
trace!("livein for block {}: {:?}", block, display_array);
}
cf.mc_mut().set_ir_block_livein(&block, livein);
let end_inst = cf.mc().get_block_range(&block).unwrap().end - 1;
let liveout = liveout[end_inst].clone().to_vec();
{
let display_array : Vec<String> = liveout.iter().map(|x| func.context.get_temp_display(*x)).collect();
trace!("liveout for block {}: {:?}", block, display_array);
}
cf.mc_mut().set_ir_block_liveout(&block, liveout);
}
}
// from Tailoring Graph-coloring Register Allocation For Runtime Compilation, Figure 4
......@@ -475,145 +702,4 @@ pub fn build_chaitin_briggs (cf: &mut CompiledFunction, func: &MuFunctionVersion
drop(_p);
info!("---finish building interference graph---");
ig
}
// from tony's code src/RegAlloc/Liveness.java
// this function is no longer used
//#[allow(dead_code)]
//pub fn build (cf: &CompiledFunction, func: &MuFunctionVersion) -> InterferenceGraph {
// let mut ig = InterferenceGraph::new();
//
// // precolor machine register nodes
// for reg in backend::all_regs().values() {
// 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();
// let mut live_in : Vec<Vec<MuID>> = vec![vec![]; n_insts];
// let mut live_out : Vec<Vec<MuID>> = vec![vec![]; n_insts];
// let mut work_list : LinkedList<usize> = LinkedList::new();
//
// // Initialize 'in' sets for each node in the flow graph
// // and creates nodes for all the involved temps/regs
// for i in 0..n_insts {
// let ref mut in_set = live_in[i];
//
// for reg_id in cf.mc().get_inst_reg_defines(i) {
// ig.new_node(reg_id, &func.context);
// }
//
// for reg_id in cf.mc().get_inst_reg_uses(i) {
// ig.new_node(reg_id, &func.context);
//
// in_set.push(reg_id);
// }
//
// work_list.push_front(i);
// }
//
// // all nodes has been added, we init graph (create adjacency matrix)
// ig.init_graph();
//
// // compute liveIn and liveOut iteratively
// trace!("build live outs");
// while !work_list.is_empty() {
// let n = work_list.pop_front().unwrap();
// trace!("build liveout for #{}", n);
// let ref mut out_set = live_out[n];
//
// // out = union(in[succ]) for all succs
// for succ in cf.mc().get_succs(n) {
// trace!("add successor's livein {:?} to #{}", &live_in[*succ], n);
// vec_utils::add_all(out_set, &live_in[*succ]);
// }
//
// // in = use(i.e. live_in) + (out - def)
// let mut diff = out_set.clone();
// for def in cf.mc().get_inst_reg_defines(n) {
// vec_utils::remove_value(&mut diff, def);
// trace!("removing def: {}", def);
// trace!("diff = {:?}", diff);
// }
// trace!("out - def = {:?}", diff);
//
// if !diff.is_empty() {
// let ref mut in_set = live_in[n];
// trace!("in = (use) {:?}", in_set);
//
// if vec_utils::add_all(in_set, &diff) {
// for p in cf.mc().get_preds(n) {
// work_list.push_front(*p);
// }
// }
// }
// trace!("in = use + (out - def) = {:?}", live_in[n]);
// }
//
// // debug live-outs
// if cfg!(debug_assertions) {
// trace!("check live-outs");
// for n in 0..n_insts {
// let ref mut live = live_out[n];
// trace!("#{}\t{:?}", n, live);
// }
// }
//
// // build interference graph
// for n in 0..n_insts {
// let ref mut live = live_out[n];
//
// let src : Option<MuID> = {
// if cf.mc().is_move(n) {
// let src = cf.mc().get_inst_reg_uses(n);
// let dst = cf.mc().get_inst_reg_defines(n);
//
// // 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 d in cf.mc().get_inst_reg_defines(n) {
// for t in live.iter() {
// if src.is_none() || (src.is_some() && *t != src.unwrap()) {
// let from = ig.get_node(d);
// let to = ig.get_node(*t);
//
// 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 d in cf.mc().get_inst_reg_defines(n) {
// vec_utils::remove_value(live, d);
// }
//
// for u in cf.mc().get_inst_reg_uses(n) {
// live.push(u);
// }
// }
//