WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.2% of users enabled 2FA.

Commit ab7dde71 authored by qinsoon's avatar qinsoon
Browse files

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