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.

Commit 43eb1d7a authored by qinsoon's avatar qinsoon
Browse files

[wip] need a global liveness analysis

(for blocks created during instruction selection)
parent f6bac8b3
#!/bin/sh
RUST_BACKTRACE=1 RUST_TEST_THREADS=1 cargo test "$@"
......@@ -152,6 +152,14 @@ impl MachineCode for ASMCode {
self.block_liveout.get(&block.to_string())
}
fn set_ir_block_livein(&mut self, block: &str, set: Vec<MuID>) {
self.block_livein.insert(block.to_string(), set);
}
fn set_ir_block_liveout(&mut self, block: &str, set: Vec<MuID>) {
self.block_liveout.insert(block.to_string(), set);
}
fn get_all_blocks(&self) -> &Vec<MuName> {
&self.blocks
}
......@@ -254,6 +262,11 @@ impl ASMCodeGen {
self.cur().code.len()
}
fn add_asm_label(&mut self, code: String) {
let l = self.line();
self.cur_mut().code.push(ASM::symbolic(code));
}
fn add_asm_block_label(&mut self, code: String, block_name: MuName) {
let l = self.line();
self.cur_mut().code.push(ASM::symbolic(code));
......@@ -376,17 +389,38 @@ impl ASMCodeGen {
}
fn prepare_reg(&self, op: &P<Value>, loc: usize) -> (String, MuID, ASMLocation) {
if cfg!(debug_assertions) {
match op.v {
Value_::SSAVar(_) => {},
_ => panic!("expecting register op")
}
}
let str = self.asm_reg_op(op);
let len = str.len();
(str, op.extract_ssa_id().unwrap(), ASMLocation::new(loc, len))
}
fn prepare_machine_reg(&self, op: &P<Value>) -> MuID {
if cfg!(debug_assertions) {
match op.v {
Value_::SSAVar(_) => {},
_ => panic!("expecting machine register op")
}
}
op.extract_ssa_id().unwrap()
}
#[allow(unused_assignments)]
fn prepare_mem(&self, op: &P<Value>, loc: usize) -> (String, Vec<MuID>, Vec<ASMLocation>) {
if cfg!(debug_assertions) {
match op.v {
Value_::Memory(_) => {},
_ => panic!("expecting register op")
}
}
let mut ids : Vec<MuID> = vec![];
let mut locs : Vec<ASMLocation> = vec![];
let mut result_str : String = "".to_string();
......@@ -526,6 +560,8 @@ impl ASMCodeGen {
let is_block_start = code.idx_to_blk.get(&i);
if is_block_start.is_none() {
if i > 0 {
trace!("inst {}: not a block start", i);
trace!("inst {}: set PREDS as previous inst {}", i, i-1);
code.preds[i].push(i - 1);
}
} else {
......@@ -538,31 +574,44 @@ impl ASMCodeGen {
if is_branch.is_some() {
// branch to target
let target = is_branch.unwrap();
trace!("inst {}: is a branch to {}", i, target);
let target_n = code.blk_to_idx.get(target).unwrap();
trace!("inst {}: branch target index is {}", i, target_n);
// cur inst's succ is target
trace!("inst {}: set SUCCS as branch target {}", i, target_n);
code.succs[i].push(*target_n);
// target's pred is cur
trace!("inst {}: set PREDS as branch source {}", target_n, i);
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();
trace!("inst {}: is a cond branch to {}", i, target);
let target_n = code.blk_to_idx.get(target).unwrap();
trace!("inst {}: branch target index is {}", i, target_n);
// cur insts' succ is target and next inst
code.succs[i].push(*target_n);
trace!("inst {}: set SUCCS as branch target {}", i, target_n);
if i < n_insts - 1 {
trace!("inst {}: set SUCCS as next inst", i + 1);
code.succs[i].push(i + 1);
}
// target's pred is cur
code.preds[*target_n].push(i);
trace!("inst {}: set PREDS as {}", *target_n, i);
} else {
// not branch nor cond branch, succ is next inst
trace!("inst {}: not a branch inst", i);
if i < n_insts - 1 {
trace!("inst {}: set SUCCS as next inst {}", i, i + 1);
code.succs[i].push(i + 1);
}
}
......@@ -725,7 +774,24 @@ impl CodeGenerator for ASMCodeGen {
fn emit_cmp_r64_mem64(&mut self, op1: &P<Value>, op2: &P<Value>) {
trace!("emit: cmp {} {}", op1, op2);
unimplemented!()
let (reg, id1, loc1) = self.prepare_reg(op1, 4 + 1);
let (mem, mut id2, mut loc2) = self.prepare_mem(op2, 4 + 1 + reg.len() + 1);
let asm = format!("cmpq {},{}", reg, mem);
// merge use vec
id2.push(id1);
loc2.push(loc1);
self.add_asm_inst(
asm,
vec![],
vec![],
id2,
loc2,
true
)
}
fn emit_mov_r64_imm32(&mut self, dest: &P<Value>, src: u32) {
......@@ -745,6 +811,7 @@ impl CodeGenerator for ASMCodeGen {
)
}
// load
fn emit_mov_r64_mem64(&mut self, dest: &P<Value>, src: &P<Value>) {
trace!("emit: mov {} -> {}", src, dest);
......@@ -763,6 +830,7 @@ impl CodeGenerator for ASMCodeGen {
)
}
// store
fn emit_mov_mem64_r64(&mut self, dest: &P<Value>, src: &P<Value>) {
trace!("emit: mov {} -> {}", src, dest);
......@@ -770,6 +838,7 @@ impl CodeGenerator for ASMCodeGen {
let (mem, mut id2, mut loc2) = self.prepare_mem(dest, 4 + 1 + reg.len() + 1);
// the register we used for the memory location is counted as 'use'
// use the vec from mem as 'use' (push use reg from src to it)
id2.push(id1);
loc2.push(loc1);
......@@ -838,6 +907,59 @@ impl CodeGenerator for ASMCodeGen {
)
}
fn emit_lea_r64(&mut self, dest: &P<Value>, src: &P<Value>) {
trace!("emit: lea {} -> {}", src, dest);
let (mem, id1, loc1) = self.prepare_mem(src, 4 + 1);
let (reg, id2, loc2) = self.prepare_reg(dest, 4 + 1 + mem.len() + 1);
let asm = format!("leaq {},{}", mem, reg);
self.add_asm_inst(
asm,
vec![id2],
vec![loc2],
id1,
loc1,
true
)
}
fn emit_and_r64_imm32(&mut self, dest: &P<Value>, src: u32) {
trace!("emit: and {}, {} -> {}", src, dest, dest);
let (reg1, id1, loc1) = self.prepare_reg(dest, 4 + 1 + 1 + src.to_string().len() + 1);
let asm = format!("andq ${},{}", src, reg1);
self.add_asm_inst(
asm,
vec![id1],
vec![loc1.clone()],
vec![id1],
vec![loc1],
false
)
}
fn emit_and_r64_r64(&mut self, dest: &P<Value>, src: &P<Value>) {
trace!("emit: and {}, {} -> {}", src, dest, dest);
let (reg1, id1, loc1) = self.prepare_reg(src, 4 + 1);
let (reg2, id2, loc2) = self.prepare_reg(dest, 4 + 1 + reg1.len() + 1);
let asm = format!("andq {},{}", reg1, reg2);
self.add_asm_inst(
asm,
vec![id2],
vec![loc2.clone()],
vec![id1, id2],
vec![loc1, loc2],
false
)
}
fn emit_add_r64_mem64(&mut self, dest: &P<Value>, src: &P<Value>) {
trace!("emit: add {}, {} -> {}", dest, src, dest);
unimplemented!()
......@@ -924,8 +1046,7 @@ impl CodeGenerator for ASMCodeGen {
unimplemented!()
}
fn emit_jmp(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_jmp(&mut self, dest_name: MuName) {
trace!("emit: jmp {}", dest_name);
// symbolic label, we dont need to patch it
......@@ -933,80 +1054,70 @@ impl CodeGenerator for ASMCodeGen {
self.add_asm_branch(asm, dest_name)
}
fn emit_je(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_je(&mut self, dest_name: MuName) {
trace!("emit: je {}", dest_name);
let asm = format!("je {}", self.asm_block_label(dest_name.clone()));
self.add_asm_branch2(asm, dest_name);
}
fn emit_jne(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_jne(&mut self, dest_name: MuName) {
trace!("emit: jne {}", dest_name);
let asm = format!("jne {}", self.asm_block_label(dest_name.clone()));
self.add_asm_branch2(asm, dest_name);
}
fn emit_ja(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_ja(&mut self, dest_name: MuName) {
trace!("emit: ja {}", dest_name);
let asm = format!("ja {}", self.asm_block_label(dest_name.clone()));
self.add_asm_branch2(asm, dest_name);
}
fn emit_jae(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_jae(&mut self, dest_name: MuName) {
trace!("emit: jae {}", dest_name);
let asm = format!("jae {}", self.asm_block_label(dest_name.clone()));
self.add_asm_branch2(asm, dest_name);
}
fn emit_jb(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_jb(&mut self, dest_name: MuName) {
trace!("emit: jb {}", dest_name);
let asm = format!("jb {}", self.asm_block_label(dest_name.clone()));
self.add_asm_branch2(asm, dest_name);
}
fn emit_jbe(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_jbe(&mut self, dest_name: MuName) {
trace!("emit: jbe {}", dest_name);
let asm = format!("jbe {}", self.asm_block_label(dest_name.clone()));
self.add_asm_branch2(asm, dest_name);
}
fn emit_jg(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_jg(&mut self, dest_name: MuName) {
trace!("emit: jg {}", dest_name);
let asm = format!("jg {}", self.asm_block_label(dest_name.clone()));
self.add_asm_branch2(asm, dest_name);
}
fn emit_jge(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_jge(&mut self, dest_name: MuName) {
trace!("emit: jge {}", dest_name);
let asm = format!("jge {}", self.asm_block_label(dest_name.clone()));
self.add_asm_branch2(asm, dest_name);
}
fn emit_jl(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_jl(&mut self, dest_name: MuName) {
trace!("emit: jl {}", dest_name);
let asm = format!("jl {}", self.asm_block_label(dest_name.clone()));
self.add_asm_branch2(asm, dest_name);
}
fn emit_jle(&mut self, dest: &Block) {
let dest_name = dest.name().unwrap();
fn emit_jle(&mut self, dest_name: MuName) {
trace!("emit: jle {}", dest_name);
let asm = format!("jle {}", self.asm_block_label(dest_name.clone()));
......
......@@ -19,11 +19,16 @@ pub trait CodeGenerator {
fn emit_cmp_r64_mem64(&mut self, op1: &P<Value>, op2: &P<Value>);
fn emit_mov_r64_imm32(&mut self, dest: &P<Value>, src: u32);
fn emit_mov_r64_mem64(&mut self, dest: &P<Value>, src: &P<Value>);
fn emit_mov_r64_mem64(&mut self, dest: &P<Value>, src: &P<Value>); // load
fn emit_mov_r64_r64(&mut self, dest: &P<Value>, src: &P<Value>);
fn emit_mov_mem64_r64(&mut self, dest: &P<Value>, src: &P<Value>);
fn emit_mov_mem64_r64(&mut self, dest: &P<Value>, src: &P<Value>); // store
fn emit_mov_mem64_imm32(&mut self, dest: &P<Value>, src: u32);
fn emit_lea_r64(&mut self, dest: &P<Value>, src: &P<Value>);
fn emit_and_r64_imm32(&mut self, dest: &P<Value>, src: u32);
fn emit_and_r64_r64(&mut self, dest: &P<Value>, src: &P<Value>);
fn emit_add_r64_r64(&mut self, dest: &P<Value>, src: &P<Value>);
fn emit_add_r64_mem64(&mut self, dest: &P<Value>, src: &P<Value>);
fn emit_add_r64_imm32(&mut self, dest: &P<Value>, src: u32);
......@@ -35,17 +40,17 @@ pub trait CodeGenerator {
fn emit_mul_r64(&mut self, src: &P<Value>);
fn emit_mul_mem64(&mut self, src: &P<Value>);
fn emit_jmp(&mut self, dest: &Block);
fn emit_je(&mut self, dest: &Block);
fn emit_jne(&mut self, dest: &Block);
fn emit_ja(&mut self, dest: &Block);
fn emit_jae(&mut self, dest: &Block);
fn emit_jb(&mut self, dest: &Block);
fn emit_jbe(&mut self, dest: &Block);
fn emit_jg(&mut self, dest: &Block);
fn emit_jge(&mut self, dest: &Block);
fn emit_jl(&mut self, dest: &Block);
fn emit_jle(&mut self, dest: &Block);
fn emit_jmp(&mut self, dest: MuName);
fn emit_je(&mut self, dest: MuName);
fn emit_jne(&mut self, dest: MuName);
fn emit_ja(&mut self, dest: MuName);
fn emit_jae(&mut self, dest: MuName);
fn emit_jb(&mut self, dest: MuName);
fn emit_jbe(&mut self, dest: MuName);
fn emit_jg(&mut self, dest: MuName);
fn emit_jge(&mut self, dest: MuName);
fn emit_jl(&mut self, dest: MuName);
fn emit_jle(&mut self, dest: MuName);
fn emit_call_near_rel32(&mut self, func: MuName);
fn emit_call_near_r64(&mut self, func: &P<Value>);
......
......@@ -10,6 +10,7 @@ use ast::types;
use ast::types::*;
use vm::VM;
use vm::CompiledFunction;
use runtime;
use runtime::mm;
use runtime::ValueLocation;
use runtime::thread;
......@@ -26,14 +27,17 @@ use std::collections::HashMap;
pub struct InstructionSelection {
name: &'static str,
backend: Box<CodeGenerator>
backend: Box<CodeGenerator>,
current_block: Option<MuName>
}
impl <'a> InstructionSelection {
pub fn new() -> InstructionSelection {
InstructionSelection{
name: "Instruction Selection (x64)",
backend: Box::new(ASMCodeGen::new())
backend: Box::new(ASMCodeGen::new()),
current_block: None
}
}
......@@ -65,7 +69,7 @@ impl <'a> InstructionSelection {
self.process_dest(&ops, fallthrough_dest, f_content, f_context, vm);
self.process_dest(&ops, branch_dest, f_content, f_context, vm);
let branch_target = f_content.get_block(branch_dest.target);
let branch_target = f_content.get_block(branch_dest.target).name().unwrap();
let ref cond = ops[cond];
......@@ -103,7 +107,7 @@ impl <'a> InstructionSelection {
self.process_dest(&ops, dest, f_content, f_context, vm);
let target = f_content.get_block(dest.target);
let target = f_content.get_block(dest.target).name().unwrap();
trace!("emit branch1");
// jmp
......@@ -416,6 +420,15 @@ impl <'a> InstructionSelection {
}
}
Instruction_::GetIRef(op_index) => {
let ops = inst.ops.read().unwrap();
let ref op = ops[op_index];
let res_tmp = self.emit_get_result(node);
self.emit_lea_base_offset(&res_tmp, &op.clone_value(), 0, vm);
}
Instruction_::ThreadExit => {
// emit a call to swap_back_to_native_stack(sp_loc: Address)
......@@ -423,20 +436,93 @@ impl <'a> InstructionSelection {
let tl = self.emit_get_threadlocal(f_content, f_context, vm);
self.backend.emit_add_r64_imm32(&tl, *thread::NATIVE_SP_LOC_OFFSET as u32);
self.emit_runtime_entry(&entrypoints::SWAP_BACK_TO_NATIVE_STACK, vec![tl.clone()], f_content, f_context, vm);
self.emit_runtime_entry(&entrypoints::SWAP_BACK_TO_NATIVE_STACK, vec![tl.clone()], None, f_content, f_context, vm);
}
Instruction_::New(ref ty) => {
let ty_info = vm.get_backend_type_info(ty.id());
let ty_size = ty_info.size;
let ty_align= ty_info.alignment;
if ty_info.size > mm::LARGE_OBJECT_THRESHOLD {
if ty_size > mm::LARGE_OBJECT_THRESHOLD {
// emit large object allocation
unimplemented!()
} else {
// emit immix allocation
// emit immix allocation fast path
// ASM: %tl = get_thread_local()
let tmp_tl = self.emit_get_threadlocal(f_content, f_context, vm);
// ASM: mov [%tl + allocator_offset + cursor_offset] -> %cursor
let cursor_offset = *thread::ALLOCATOR_OFFSET + *mm::ALLOCATOR_CURSOR_OFFSET;
let tmp_cursor = self.make_temporary(f_context, runtime::ADDRESS_TYPE.clone(), vm);
self.emit_load_base_offset(&tmp_cursor, &tmp_tl, cursor_offset as u32, vm);
// alignup cursor (cursor + align - 1 & !(align - 1))
// ASM: lea align-1(%cursor) -> %start
let align = ty_info.alignment;
let tmp_start = self.make_temporary(f_context, runtime::ADDRESS_TYPE.clone(), vm);
self.emit_lea_base_offset(&tmp_start, &tmp_tl, (align - 1) as u32, vm);
// ASM: and %start, !(align-1) -> %start
self.backend.emit_and_r64_imm32(&tmp_start, !(align-1) as u32);
// bump cursor
// ASM: lea size(%start) -> %end
let tmp_end = self.make_temporary(f_context, runtime::ADDRESS_TYPE.clone(), vm);
self.emit_lea_base_offset(&tmp_end, &tmp_start, ty_size as u32, vm);
// check with limit
// ASM: cmp %end, [%tl + allocator_offset + limit_offset]
let limit_offset = *thread::ALLOCATOR_OFFSET + *mm::ALLOCATOR_LIMIT_OFFSET;
let mem_limit = self.make_memory_op_base_offset(&tmp_tl, limit_offset as u32, runtime::ADDRESS_TYPE.clone(), vm);
self.backend.emit_cmp_r64_mem64(&tmp_end, &mem_limit);
// branch to slow path if end > limit
// ASM: jg alloc_slow
let slowpath = format!("{}_allocslow", node.id());
self.backend.emit_jg(slowpath.clone());
// update cursor
// ASM: mov %end -> [%tl + allocator_offset + limit_offset]
self.emit_store_base_offset(&tmp_tl, limit_offset as u32, &tmp_end, vm);
// put start as result
// ASM: mov %start -> %result
let tmp_res = self.emit_get_result(node);
self.backend.emit_mov_r64_r64(&tmp_res, &tmp_start);
// ASM jmp alloc_end
let allocend = format!("{}_allocend", node.id());
self.backend.emit_jmp(allocend.clone());
// finishing current block
self.backend.end_block(self.current_block.as_ref().unwrap().clone());
// alloc_slow:
// call alloc_slow(size, align) -> %ret
// new block (no livein)
self.current_block = Some(slowpath.clone());
self.backend.start_block(slowpath.clone());
self.backend.set_block_livein(slowpath.clone(), &vec![]);
// get allocator
let tl = self.emit_get_threadlocal(f_content, f_context, vm);
let const_size = self.make_value_int_const(ty_size as u64, vm);
let const_align= self.make_value_int_const(ty_align as u64, vm);
let rets = self.emit_runtime_entry(
&entrypoints::ALLOC_SLOW,
vec![const_size, const_align],
Some(vec![
tmp_res.clone()
]),
f_content, f_context, vm
);
// end block (no liveout)
self.backend.end_block(slowpath.clone());
self.backend.set_block_liveout(slowpath.clone(), &vec![tmp_res.clone()]);
// block: alloc_end
self.backend.start_block(allocend.clone());
self.current_block = Some(allocend.clone());
}
}
......@@ -450,13 +536,60 @@ impl <'a> InstructionSelection {
}
}
fn make_temporary(&mut self, f_context: &mut FunctionContext, ty: P<MuType>, vm: &VM) -> P<Value> {
f_context.make_temporary(vm.next_id(), ty).clone_value()
}
fn make_memory_op_base_offset (&mut self, base: &P<Value>, offset: u32, ty: P<MuType>, vm: &VM) -> P<Value> {
P(Value{
hdr: MuEntityHeader::unnamed(vm.next_id()),
ty: ty.clone(),
v: Value_::Memory(MemoryLocation::Address{
base: base.clone(),
offset: Some(self.make_value_int_const(offset as u64, vm)),
index: None,
scale: None
})
})
}
fn make_value_int_const (&mut self, val: u64, vm: &VM) -> P<Value> {
P(Value{
hdr: MuEntityHeader::unnamed(vm.next_id()),
ty: runtime::UINT64_TYPE.clone(),
v: Value_::Constant(Constant::Int(val))
})
}
fn emit_load_base_offset (&mut self, dest: &P<Value>, base: &P<Value>, offset: u32, vm: &VM) {
let mem = self.make_memory_op_base_offset(base, offset, dest.ty.clone(), vm);
self.backend.emit_mov_r64_mem64(dest, &mem);
}
fn emit_store_base_offset (&mut self, base: &P<Value>, offset: u32, src: &P<Value>, vm: &VM) {
let mem = self.make_memory_op_base_offset(base, offset, src.ty.clone(), vm);
self.backend.emit_mov_mem64_r64(&mem, src);
}
fn emit_lea_base_offset (&mut self, dest: &P<Value>, base: &P<Value>, offset: u32, vm: &VM) {
let mem = self.make_memory_op_base_offset(base, offset, runtime::ADDRESS_TYPE.clone(), vm);