Commit ab7dde71 authored by qinsoon's avatar qinsoon

control flow analysis for asm backend

parent dbb70ac9
......@@ -2,16 +2,13 @@
use compiler::backend::x86_64;
use compiler::backend::x86_64::CodeGenerator;
use vm::MachineCode;
use vm::CompiledFunction;
use vm::machine_code::MachineCode;
use ast::ptr::P;
use ast::ir::*;
use ast::types::*;
use ast::inst::*;
use std::collections::HashMap;
use std::fmt;
use std::str;
use std::usize;
......@@ -19,7 +16,15 @@ struct ASMCode {
name: MuTag,
code: Vec<ASM>,
reg_defines: HashMap<MuID, Vec<ASMLocation>>,
reg_uses: HashMap<MuID, Vec<ASMLocation>>
reg_uses: HashMap<MuID, Vec<ASMLocation>>,
preds: Vec<Vec<usize>>,
succs: Vec<Vec<usize>>,
idx_to_blk: HashMap<usize, MuTag>,
blk_to_idx: HashMap<MuTag, usize>,
cond_branches: HashMap<usize, MuTag>,
branches: HashMap<usize, MuTag>
}
impl MachineCode for ASMCode {
......@@ -35,18 +40,25 @@ impl MachineCode for ASMCode {
}
}
fn get_inst_reg_uses(&self, index: usize) -> Vec<MuID> {
unimplemented!()
}
fn get_inst_reg_defines(&self, index: usize) -> Vec<MuID> {
unimplemented!()
fn get_inst_reg_uses(&self, index: usize) -> &Vec<MuID> {
&self.code[index].defines
}
fn get_reg_uses(&self, id: MuID) -> Vec<MuID> {
unimplemented!()
fn get_inst_reg_defines(&self, index: usize) -> &Vec<MuID> {
&self.code[index].uses
}
fn get_reg_defines(&self, id: MuID) -> Vec<MuID> {
unimplemented!()
fn print(&self) {
println!("");
println!("code for {}: ", self.name);
let n_insts = self.code.len();
for i in 0..n_insts {
let ref line = self.code[i];
println!("#{}\t{}\t\tpred: {:?}, succ: {:?}", i, line.code, self.preds[i], self.succs[i]);
}
println!("");
}
}
......@@ -80,9 +92,25 @@ impl ASM {
uses: vec![]
}
}
fn call(line: String) -> ASM {
ASM {
code: line,
defines: vec![],
uses: vec![]
}
}
fn ret(line: String) -> ASM {
ASM {
code: line,
defines: vec![],
uses: vec![]
}
}
}
#[derive(Clone)]
#[derive(Clone, Debug)]
struct ASMLocation {
line: usize,
index: usize,
......@@ -101,9 +129,7 @@ impl ASMLocation {
}
pub struct ASMCodeGen {
cur: Option<ASMCode>,
all_code: HashMap<MuTag, ASMCode>
cur: Option<Box<ASMCode>>
}
const REG_PLACEHOLDER_LEN : usize = 3;
......@@ -118,8 +144,7 @@ lazy_static! {
impl ASMCodeGen {
pub fn new() -> ASMCodeGen {
ASMCodeGen {
cur: None,
all_code: HashMap::new()
cur: None
}
}
......@@ -131,6 +156,10 @@ impl ASMCodeGen {
self.cur.as_mut().unwrap()
}
fn line(&self) -> usize {
self.cur().code.len()
}
fn replace(s: &mut String, index: usize, replace: &str, replace_len: usize) {
let vec = unsafe {s.as_mut_vec()};
......@@ -143,24 +172,86 @@ impl ASMCodeGen {
}
}
/// return line number for this code
fn add_asm_block_label(&mut self, code: String, block_name: &'static str) {
let l = self.line();
self.cur_mut().code.push(ASM::symbolic(code));
self.cur_mut().idx_to_blk.insert(l, block_name);
self.cur_mut().blk_to_idx.insert(block_name, l);
}
fn add_asm_symbolic(&mut self, code: String){
self.cur_mut().code.push(ASM::symbolic(code));
}
fn add_asm_branch(&mut self, code: String) {
fn add_asm_call(&mut self, code: String) {
self.cur_mut().code.push(ASM::call(code));
}
fn add_asm_ret(&mut self, code: String) {
self.cur_mut().code.push(ASM::ret(code));
}
fn add_asm_branch(&mut self, code: String, target: &'static str) {
let l = self.line();
self.cur_mut().code.push(ASM::branch(code));
self.cur_mut().branches.insert(l, target);
}
fn add_asm_branch2(&mut self, code: String, target: &'static str) {
let l = self.line();
self.cur_mut().code.push(ASM::branch(code));
self.cur_mut().cond_branches.insert(l, target);
}
fn add_asm_inst(
&mut self,
code: String,
defines: Vec<MuID>,
define_locs: Vec<ASMLocation>,
mut define_locs: Vec<ASMLocation>,
uses: Vec<MuID>,
use_locs: Vec<ASMLocation>)
mut use_locs: Vec<ASMLocation>)
{
let line = self.line();
trace!("asm: {}", code);
trace!(" defines: {:?}, def_locs: {:?}", defines, define_locs);
trace!(" uses: {:?}, use_locs: {:?}", uses, use_locs);
let mc = self.cur_mut();
// add locations of defined registers
for i in 0..define_locs.len() {
let id = defines[i];
// update line in location
let ref mut loc = define_locs[i];
loc.line = line;
if mc.reg_defines.contains_key(&id) {
mc.reg_defines.get_mut(&id).unwrap().push(loc.clone());
} else {
mc.reg_defines.insert(id, vec![loc.clone()]);
}
}
for i in 0..use_locs.len() {
let id = uses[i];
// update line in location
let ref mut loc = use_locs[i];
loc.line = line;
if mc.reg_uses.contains_key(&id) {
mc.reg_defines.get_mut(&id).unwrap().push(loc.clone());
} else {
mc.reg_defines.insert(id, vec![loc.clone()]);
}
}
// put the instruction
mc.code.push(ASM::inst(code, defines, uses));
}
fn define_reg(&mut self, reg: &P<Value>, loc: ASMLocation) {
......@@ -195,7 +286,7 @@ impl ASMCodeGen {
fn prepare_machine_reg(&self, op: &P<Value>) -> MuID {
op.extract_ssa_id().unwrap()
}
}
fn asm_reg_op(&self, op: &P<Value>) -> String {
let id = op.extract_ssa_id().unwrap();
......@@ -211,24 +302,95 @@ impl ASMCodeGen {
fn asm_block_label(&self, label: MuTag) -> String {
format!("{}_{}", self.cur().name, label)
}
fn control_flow_analysis(&mut self) {
// control flow analysis
let n_insts = self.line();
let code = self.cur_mut();
code.preds = vec![vec![]; n_insts];
code.succs = vec![vec![]; n_insts];
for i in 0..n_insts {
// determine predecessor - if cur is not block start, its predecessor is previous insts
let is_block_start = code.idx_to_blk.get(&i);
if is_block_start.is_none() {
if i > 0 {
code.preds[i].push(i - 1);
}
} 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
let is_branch = code.branches.get(&i);
if is_branch.is_some() {
// branch to target
let target = is_branch.unwrap();
let target_n = code.blk_to_idx.get(target).unwrap();
// cur inst's succ is target
code.succs[i].push(*target_n);
// target's pred is cur
code.preds[*target_n].push(i);
} else {
let is_cond_branch = code.cond_branches.get(&i);
if is_cond_branch.is_some() {
// branch to target
let target = is_cond_branch.unwrap();
let target_n = code.blk_to_idx.get(target).unwrap();
// cur insts' succ is target and next inst
code.succs[i].push(*target_n);
if i < n_insts - 1 {
code.succs[i].push(i + 1);
}
// target's pred is cur
code.preds[*target_n].push(i);
} else {
// not branch nor cond branch, succ is next inst
if i < n_insts - 1 {
code.succs[i].push(i + 1);
}
}
}
}
// a sanity check for fallthrough blocks
for i in 0..n_insts {
if i != 0 && code.preds[i].len() == 0 {
code.preds[i].push(i - 1);
}
}
}
}
impl CodeGenerator for ASMCodeGen {
fn start_code(&mut self, func_name: MuTag) {
self.cur = Some(ASMCode {
self.cur = Some(Box::new(ASMCode {
name: func_name,
code: vec![],
reg_defines: HashMap::new(),
reg_uses: HashMap::new()
});
reg_uses: HashMap::new(),
preds: vec![],
succs: vec![],
idx_to_blk: HashMap::new(),
blk_to_idx: HashMap::new(),
cond_branches: HashMap::new(),
branches: HashMap::new()
}));
self.add_asm_symbolic(format!(".globl {}", func_name));
}
fn finish_code(&mut self) {
let finish = self.cur.take().unwrap();
self.all_code.insert(finish.name, finish);
fn finish_code(&mut self) -> Box<MachineCode> {
self.control_flow_analysis();
self.cur.take().unwrap()
}
fn print_cur_code(&self) {
......@@ -238,8 +400,10 @@ impl CodeGenerator for ASMCodeGen {
let code = self.cur.as_ref().unwrap();
println!("code for {}: ", code.name);
for line in code.code.iter() {
println!("{}", line.code);
let n_insts = code.code.len();
for i in 0..n_insts {
let ref line = code.code[i];
println!("#{}\t{}", i, line.code);
}
} else {
println!("no current code");
......@@ -249,8 +413,8 @@ impl CodeGenerator for ASMCodeGen {
}
fn start_block(&mut self, block_name: MuTag) {
let label = format!("{}:", self.asm_block_label(block_name));
self.add_asm_symbolic(label);
let label = format!("{}:", self.asm_block_label(block_name));
self.add_asm_block_label(label, block_name);
}
fn emit_cmp_r64_r64(&mut self, op1: &P<Value>, op2: &P<Value>) {
......@@ -433,84 +597,84 @@ impl CodeGenerator for ASMCodeGen {
// symbolic label, we dont need to patch it
let asm = format!("jmp {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm)
self.add_asm_branch(asm, dest.target)
}
fn emit_je(&mut self, dest: &Destination) {
trace!("emit: je {}", dest.target);
let asm = format!("je {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm);
self.add_asm_branch2(asm, dest.target);
}
fn emit_jne(&mut self, dest: &Destination) {
trace!("emit: jne {}", dest.target);
let asm = format!("jne {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm);
self.add_asm_branch2(asm, dest.target);
}
fn emit_ja(&mut self, dest: &Destination) {
trace!("emit: ja {}", dest.target);
let asm = format!("ja {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm);
self.add_asm_branch2(asm, dest.target);
}
fn emit_jae(&mut self, dest: &Destination) {
trace!("emit: jae {}", dest.target);
let asm = format!("jae {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm);
self.add_asm_branch2(asm, dest.target);
}
fn emit_jb(&mut self, dest: &Destination) {
trace!("emit: jb {}", dest.target);
let asm = format!("jb {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm);
self.add_asm_branch2(asm, dest.target);
}
fn emit_jbe(&mut self, dest: &Destination) {
trace!("emit: jbe {}", dest.target);
let asm = format!("jbe {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm);
self.add_asm_branch2(asm, dest.target);
}
fn emit_jg(&mut self, dest: &Destination) {
trace!("emit: jg {}", dest.target);
let asm = format!("jg {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm);
self.add_asm_branch2(asm, dest.target);
}
fn emit_jge(&mut self, dest: &Destination) {
trace!("emit: jge {}", dest.target);
let asm = format!("jge {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm);
self.add_asm_branch2(asm, dest.target);
}
fn emit_jl(&mut self, dest: &Destination) {
trace!("emit: jl {}", dest.target);
let asm = format!("jl {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm);
self.add_asm_branch2(asm, dest.target);
}
fn emit_jle(&mut self, dest: &Destination) {
trace!("emit: jle {}", dest.target);
let asm = format!("jle {}", self.asm_block_label(dest.target));
self.add_asm_branch(asm);
self.add_asm_branch2(asm, dest.target);
}
fn emit_call_near_rel32(&mut self, func: MuTag) {
trace!("emit: call {}", func);
let asm = format!("call {}", func);
self.add_asm_branch(asm);
self.add_asm_call(asm);
// FIXME: call interferes with machine registers
}
......@@ -529,7 +693,7 @@ impl CodeGenerator for ASMCodeGen {
trace!("emit: ret");
let asm = format!("ret");
self.add_asm_branch(asm);
self.add_asm_ret(asm);
}
fn emit_push_r64(&mut self, src: &P<Value>) {
......
......@@ -2,9 +2,11 @@ use ast::ptr::P;
use ast::ir::*;
use ast::inst::*;
use vm::machine_code::MachineCode;
pub trait CodeGenerator {
fn start_code(&mut self, func_name: MuTag);
fn finish_code(&mut self);
fn finish_code(&mut self) -> Box<MachineCode>;
fn print_cur_code(&self);
......
......@@ -9,6 +9,7 @@ use ast::op::OpCode;
use ast::types;
use ast::types::MuType_;
use vm::context::VMContext;
use vm::machine_code::CompiledFunction;
use compiler::CompilerPass;
use compiler::backend::x86_64;
......@@ -123,7 +124,7 @@ impl <'a> InstructionSelection {
debug_assert!(func_sig.arg_tys.len() == rets.len());
let mut gpr_arg_count = 0;
let mut fpr_arg_count = 0;
// TODO: let mut fpr_arg_count = 0;
for arg_index in data.args.iter() {
let ref arg = ops[*arg_index];
trace!("arg {}", arg);
......@@ -172,7 +173,7 @@ impl <'a> InstructionSelection {
// deal with ret vals
let mut gpr_ret_count = 0;
let mut fpr_ret_count = 0;
// TODO: let mut fpr_ret_count = 0;
for val in rets {
if val.is_int_reg() {
if gpr_ret_count < x86_64::RETURN_GPRs.len() {
......@@ -386,7 +387,7 @@ impl <'a> InstructionSelection {
// unload arguments
let mut gpr_arg_count = 0;
let mut fpr_arg_count = 0;
// TODO: let mut fpr_arg_count = 0;
for arg in args {
if arg.is_int_reg() {
if gpr_arg_count < x86_64::ARGUMENT_GPRs.len() {
......@@ -419,7 +420,7 @@ impl <'a> InstructionSelection {
};
let mut gpr_ret_count = 0;
let mut fpr_ret_count = 0;
// TODO: let mut fpr_ret_count = 0;
for i in ret_val_indices {
let ref ret_val = ops[*i];
if self.match_ireg(ret_val) {
......@@ -532,6 +533,7 @@ impl <'a> InstructionSelection {
}
}
#[allow(unused_variables)]
fn match_fpreg(&mut self, op: &P<TreeNode>) -> bool {
unimplemented!()
}
......@@ -583,10 +585,12 @@ impl <'a> InstructionSelection {
}
}
#[allow(unused_variables)]
fn match_mem(&mut self, op: &P<TreeNode>) -> bool {
unimplemented!()
}
#[allow(unused_variables)]
fn emit_mem(&mut self, op: &P<TreeNode>) -> P<Value> {
unimplemented!()
}
......@@ -650,6 +654,12 @@ impl CompilerPass for InstructionSelection {
fn finish_function(&mut self, vm_context: &VMContext, func: &mut MuFunction) {
self.backend.print_cur_code();
self.backend.finish_code();
let mc = self.backend.finish_code();
let compiled_func = CompiledFunction {
fn_name: func.fn_name,
mc: mc
};
vm_context.add_compiled_func(compiled_func);
}
}
\ No newline at end of file
......@@ -2,7 +2,9 @@ pub mod inst_sel;
pub mod reg_alloc;
#[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;
\ No newline at end of file
use vm::machine_code::CompiledFunction;
use vm::machine_code::MachineCode;
use ast::ir::*;
use std::collections::LinkedList;
pub struct InterferenceGraph {
foo: usize
}
impl InterferenceGraph {
fn new() -> InterferenceGraph {
InterferenceGraph {foo: 0}
}
fn new_node(&mut self, node: MuID) {
}
}
// from tony's code src/RegAlloc/Liveness.java
pub fn build (cf: &CompiledFunction, f: &MuFunction) {
let mut ig = InterferenceGraph::new();
// FIXME: precolor nodes
// 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);
}
for reg_id in cf.mc.get_inst_reg_uses(i) {
ig.new_node(*reg_id);
in_set.push(*reg_id);
}
work_list.push_front(i);
}
while !work_list.is_empty() {
let n = work_list.pop_front().unwrap();
let ref in_set = live_in[n];
let ref mut out_set = live_out[n];
}
}
\ No newline at end of file