Commit 1dbd1d9b authored by Yi Lin's avatar Yi Lin

Merge branch 'domtree' into 'develop'

multiple fixes and improvements

See merge request !46
parents 49182e46 c9de0874
...@@ -65,6 +65,18 @@ testmuc:test_swapstack: ...@@ -65,6 +65,18 @@ testmuc:test_swapstack:
- *build_muc - *build_muc
- LD_LIBRARY_PATH=mu-tool-compiler/lib/ MUC=mu-tool-compiler/muc python2 -m pytest test_thread_and_stack.py -v - LD_LIBRARY_PATH=mu-tool-compiler/lib/ MUC=mu-tool-compiler/muc python2 -m pytest test_thread_and_stack.py -v
testmuc:test_cmp:
stage: test
script:
- *build_muc
- LD_LIBRARY_PATH=mu-tool-compiler/lib/ MUC=mu-tool-compiler/muc python2 -m pytest test_cmp.py -v
testmuc:test_binop:
stage: test
script:
- *build_muc
- LD_LIBRARY_PATH=mu-tool-compiler/lib/ MUC=mu-tool-compiler/muc python2 -m pytest test_binop.py -v
testjit:milestones: testjit:milestones:
stage: test stage: test
script: script:
......
...@@ -441,6 +441,15 @@ impl MuType { ...@@ -441,6 +441,15 @@ impl MuType {
} }
} }
/// gets the function signature for FuncRef or UFuncPtr, return None if the type is not
/// those two types
pub fn get_func_sig(&self) -> Option<P<MuFuncSig>> {
match self.v {
MuType_::FuncRef(ref sig) | MuType_::UFuncPtr(ref sig) => Some(sig.clone()),
_ => None
}
}
/// gets the length (in bit) of a integer/pointer type (assume pointer types are always 64 bits) /// gets the length (in bit) of a integer/pointer type (assume pointer types are always 64 bits)
// FIXME: should deprecate this function, and get the length from BackendType // FIXME: should deprecate this function, and get the length from BackendType
pub fn get_int_length(&self) -> Option<usize> { pub fn get_int_length(&self) -> Option<usize> {
......
...@@ -4743,15 +4743,12 @@ impl<'a> InstructionSelection { ...@@ -4743,15 +4743,12 @@ impl<'a> InstructionSelection {
let ref ops = inst.ops; let ref ops = inst.ops;
let ref func = ops[calldata.func]; let ref func = ops[calldata.func];
let ref func_sig = match func.v { let func_sig = {
TreeNode_::Value(ref pv) => { let t = func.ty();
let ty: &MuType = &pv.ty; match t.v {
match ty.v { MuType_::FuncRef(ref sig) | MuType_::UFuncPtr(ref sig) => sig.clone(),
MuType_::FuncRef(ref sig) | MuType_::UFuncPtr(ref sig) => sig, _ => panic!("expected funcref/ufuncptr type")
_ => panic!("expected funcref/ptr type")
}
} }
_ => panic!("expected funcref/ptr type")
}; };
debug_assert!(func_sig.arg_tys.len() == calldata.args.len()); debug_assert!(func_sig.arg_tys.len() == calldata.args.len());
......
...@@ -1731,17 +1731,17 @@ impl ASMCodeGen { ...@@ -1731,17 +1731,17 @@ impl ASMCodeGen {
let inst = inst.to_string() + &op_postfix(len); let inst = inst.to_string() + &op_postfix(len);
trace!("emit: {} {} {}", inst, op1, op2); trace!("emit: {} {} {}", inst, op1, op2);
let (mem, mut uses) = self.prepare_mem(op2, inst.len() + 1); let (reg, id, loc) = self.prepare_reg(op1, inst.len() + 1);
let (reg, id1, loc1) = self.prepare_reg(op1, inst.len() + 1 + mem.len() + 1); let (mem, mut uses) = self.prepare_mem(op2, inst.len() + 1 + reg.len() + 1);
if uses.contains_key(&id1) { if uses.contains_key(&id) {
let mut locs = uses.get_mut(&id1).unwrap(); let mut locs = uses.get_mut(&id).unwrap();
vec_utils::add_unique(locs, loc1.clone()); vec_utils::add_unique(locs, loc.clone());
} else { } else {
uses.insert(id1, vec![loc1.clone()]); uses.insert(id, vec![loc.clone()]);
} }
let asm = format!("{} {},{}", inst, mem, reg); let asm = format!("{} {},{}", inst, reg, mem);
self.add_asm_inst(asm, linked_hashmap!{}, uses, true) self.add_asm_inst(asm, linked_hashmap!{}, uses, true)
} }
...@@ -1832,10 +1832,7 @@ impl ASMCodeGen { ...@@ -1832,10 +1832,7 @@ impl ASMCodeGen {
/// emits an instruction (use 1 reg 1 mem, define the reg) /// emits an instruction (use 1 reg 1 mem, define the reg)
fn internal_binop_def_r_mem(&mut self, inst: &str, dest: &P<Value>, src: &P<Value>) { fn internal_binop_def_r_mem(&mut self, inst: &str, dest: &P<Value>, src: &P<Value>) {
let len = match dest.ty.get_int_length() { let len = check_op_len(dest);
Some(n) if n == 64 | 32 | 16 | 8 => n,
_ => panic!("unimplemented int types: {}", dest.ty)
};
let inst = inst.to_string() + &op_postfix(len); let inst = inst.to_string() + &op_postfix(len);
trace!("emit: {} {}, {} -> {}", inst, src, dest, dest); trace!("emit: {} {}, {} -> {}", inst, src, dest, dest);
...@@ -2256,10 +2253,26 @@ impl ASMCodeGen { ...@@ -2256,10 +2253,26 @@ impl ASMCodeGen {
) )
} }
/// emits an instruction (use 2 fpregs, define 1st fpreg) /// emits an instruction (use 1 fpreg 1 memory operand, define the fpreg)
fn internal_fp_binop_def_r_mem(&mut self, inst: &str, dest: Reg, src: Reg) { fn internal_fp_binop_def_r_mem(&mut self, inst: &str, dest: Reg, src: Mem) {
trace!("emit: {} {}, {} -> {}", inst, src, dest, dest); trace!("emit: {} {}, {} -> {}", inst, src, dest, dest);
unimplemented!()
let (mem, mut uses) = self.prepare_mem(src, inst.len() + 1);
let (reg, id, loc) = self.prepare_fpreg(dest, inst.len() + 1 + mem.len() + 1);
// uses are GPRs, it won't include FPRs - we can simply insert into the map
uses.insert(id, vec![loc.clone()]);
let asm = format!("{} {},{}", inst, mem, reg);
self.add_asm_inst(
asm,
linked_hashmap!{
id => vec![loc]
},
uses,
true
)
} }
/// emits a move instruction (reg -> fpreg) /// emits a move instruction (reg -> fpreg)
...@@ -2523,6 +2536,10 @@ impl CodeGenerator for ASMCodeGen { ...@@ -2523,6 +2536,10 @@ impl CodeGenerator for ASMCodeGen {
self.internal_binop_no_def_mem_r("cmp", op1, op2) self.internal_binop_no_def_mem_r("cmp", op1, op2)
} }
fn emit_cmp_r_mem(&mut self, op1: &P<Value>, op2: &P<Value>) {
self.internal_binop_no_def_r_mem("cmp", op1, op2)
}
fn emit_test_r_r(&mut self, op1: &P<Value>, op2: &P<Value>) { fn emit_test_r_r(&mut self, op1: &P<Value>, op2: &P<Value>) {
self.internal_binop_no_def_r_r("test", op1, op2) self.internal_binop_no_def_r_r("test", op1, op2)
} }
...@@ -4464,7 +4481,12 @@ pub fn spill_rewrite( ...@@ -4464,7 +4481,12 @@ pub fn spill_rewrite(
.clone_value(); .clone_value();
// maintain mapping // maintain mapping
trace!("reg {} used in Inst{} is replaced as {}", val_reg, i, temp); trace!(
"reg {} used in Inst{} is replaced as {}",
val_reg.id(),
i,
temp
);
spilled_scratch_temps.insert(temp.id(), reg); spilled_scratch_temps.insert(temp.id(), reg);
// generate a load // generate a load
...@@ -4521,7 +4543,7 @@ pub fn spill_rewrite( ...@@ -4521,7 +4543,7 @@ pub fn spill_rewrite(
}; };
trace!( trace!(
"reg {} defined in Inst{} is replaced as {}", "reg {} defined in Inst{} is replaced as {}",
val_reg, val_reg.id(),
i, i,
temp temp
); );
......
...@@ -61,7 +61,8 @@ pub trait CodeGenerator { ...@@ -61,7 +61,8 @@ pub trait CodeGenerator {
// comparison // comparison
fn emit_cmp_r_r(&mut self, op1: Reg, op2: Reg); fn emit_cmp_r_r(&mut self, op1: Reg, op2: Reg);
fn emit_cmp_imm_r(&mut self, op1: i32, op2: Reg); fn emit_cmp_imm_r(&mut self, op1: i32, op2: Reg);
fn emit_cmp_mem_r(&mut self, op1: Reg, op2: Reg); fn emit_cmp_mem_r(&mut self, op1: Mem, op2: Reg);
fn emit_cmp_r_mem(&mut self, op1: Reg, op2: Mem);
fn emit_test_r_r(&mut self, op1: Reg, op2: Reg); fn emit_test_r_r(&mut self, op1: Reg, op2: Reg);
fn emit_test_imm_r(&mut self, op1: i32, op2: Reg); fn emit_test_imm_r(&mut self, op1: i32, op2: Reg);
......
This diff is collapsed.
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
/// A instruction selection pass. Uses simple tree pattern matching. /// A instruction selection pass. Uses simple tree pattern matching.
pub mod inst_sel; pub mod inst_sel;
/// A Dominator Tree pass for machine code.
pub mod mc_loopanalysis;
/// A register allocation pass. Graph coloring. /// A register allocation pass. Graph coloring.
pub mod reg_alloc; pub mod reg_alloc;
/// A peephole optimization pass after register allocation. /// A peephole optimization pass after register allocation.
......
...@@ -14,13 +14,15 @@ ...@@ -14,13 +14,15 @@
use ast::ir::*; use ast::ir::*;
use ast::ptr::*; use ast::ptr::*;
use compiler;
use compiler::frame::*; use compiler::frame::*;
use compiler::backend::mc_loopanalysis::MCLoopAnalysisResult;
use runtime::ValueLocation; use runtime::ValueLocation;
use rodal;
use utils::Address; use utils::Address;
use std::sync::Arc; use utils::{LinkedHashMap, LinkedHashSet};
use runtime::resolve_symbol; use runtime::resolve_symbol;
use rodal;
use std::sync::Arc;
use std; use std;
use std::ops; use std::ops;
use std::collections::HashMap; use std::collections::HashMap;
...@@ -52,7 +54,10 @@ pub struct CompiledFunction { ...@@ -52,7 +54,10 @@ pub struct CompiledFunction {
/// start location of this compiled function /// start location of this compiled function
pub start: ValueLocation, pub start: ValueLocation,
/// end location of this compiled function /// end location of this compiled function
pub end: ValueLocation pub end: ValueLocation,
/// results of machine code loop analysis
pub loop_analysis: Option<Box<MCLoopAnalysisResult>>
} }
rodal_named!(CompiledFunction); rodal_named!(CompiledFunction);
unsafe impl rodal::Dump for CompiledFunction { unsafe impl rodal::Dump for CompiledFunction {
...@@ -90,7 +95,8 @@ impl CompiledFunction { ...@@ -90,7 +95,8 @@ impl CompiledFunction {
mc: Some(mc), mc: Some(mc),
frame: frame, frame: frame,
start: start_loc, start: start_loc,
end: end_loc end: end_loc,
loop_analysis: None
} }
} }
...@@ -215,6 +221,15 @@ pub trait MachineCode { ...@@ -215,6 +221,15 @@ pub trait MachineCode {
fn get_all_blocks(&self) -> Vec<MuName>; fn get_all_blocks(&self) -> Vec<MuName>;
/// gets the entry block /// gets the entry block
fn get_entry_block(&self) -> MuName; fn get_entry_block(&self) -> MuName;
/// gets the prologue block
fn get_prologue_block(&self) -> MuName {
for name in self.get_all_blocks() {
if name.contains(compiler::PROLOGUE_BLOCK_NAME) {
return name;
}
}
unreachable!()
}
/// gets the range of a given block, returns [start_inst, end_inst) (end_inst not included) /// gets the range of a given block, returns [start_inst, end_inst) (end_inst not included)
fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>>; fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>>;
/// gets the block for a given index, returns an Option for the block /// gets the block for a given index, returns an Option for the block
...@@ -240,4 +255,183 @@ pub trait MachineCode { ...@@ -240,4 +255,183 @@ pub trait MachineCode {
fn patch_frame_size(&mut self, size: usize); fn patch_frame_size(&mut self, size: usize);
fn as_any(&self) -> &Any; fn as_any(&self) -> &Any;
fn build_cfg(&self) -> MachineCFG {
let mut ret = MachineCFG::empty();
let all_blocks = self.get_all_blocks();
let (start_inst_map, end_inst_map) = {
let mut start_inst_map: LinkedHashMap<usize, MuName> = LinkedHashMap::new();
let mut end_inst_map: LinkedHashMap<usize, MuName> = LinkedHashMap::new();
for block in all_blocks.iter() {
let range = match self.get_block_range(block) {
Some(range) => range,
None => panic!("cannot find range for block {}", block)
};
// start inst
let first_inst = range.start;
// last inst (we need to skip symbols)
let last_inst = match self.get_last_inst(range.end) {
Some(last) => last,
None => {
panic!(
"cannot find last instruction in block {}, \
this block contains no instruction?",
block
)
}
};
trace!(
"Block {}: start_inst={}, end_inst(inclusive)={}",
block,
first_inst,
last_inst
);
start_inst_map.insert(first_inst, block.clone());
end_inst_map.insert(last_inst, block.clone());
}
(start_inst_map, end_inst_map)
};
// collect info for each basic block
for block in self.get_all_blocks().iter() {
let range = self.get_block_range(block).unwrap();
let start_inst = range.start;
let end = range.end;
let preds: Vec<MuName> = {
let mut ret = vec![];
// predecessors of the first instruction is the predecessors of this block
for pred in self.get_preds(start_inst).into_iter() {
match end_inst_map.get(pred) {
Some(block) => ret.push(block.clone()),
None => {}
}
}
ret
};
let succs: Vec<MuName> = {
let mut ret = vec![];
// successors of the last instruction is the successors of this block
for succ in self.get_succs(self.get_last_inst(end).unwrap()).into_iter() {
match start_inst_map.get(succ) {
Some(block) => ret.push(block.clone()),
None => {}
}
}
ret
};
let node = MachineCFGNode {
block: block.clone(),
preds: preds,
succs: succs
};
trace!("{:?}", node);
ret.inner.insert(block.clone(), node);
}
ret
}
}
pub struct MachineCFG {
inner: LinkedHashMap<MuName, MachineCFGNode>
}
impl MachineCFG {
fn empty() -> Self {
MachineCFG {
inner: LinkedHashMap::new()
}
}
pub fn get_blocks(&self) -> Vec<MuName> {
self.inner.keys().map(|x| x.clone()).collect()
}
pub fn get_preds(&self, block: &MuName) -> &Vec<MuName> {
&self.inner.get(block).unwrap().preds
}
pub fn get_succs(&self, block: &MuName) -> &Vec<MuName> {
&self.inner.get(block).unwrap().succs
}
pub fn has_edge(&self, from: &MuName, to: &MuName) -> bool {
if self.inner.contains_key(from) {
let ref node = self.inner.get(from).unwrap();
for succ in node.succs.iter() {
if succ == to {
return true;
}
}
}
false
}
/// checks if there exists a path between from and to, without excluded node
pub fn has_path_with_node_excluded(
&self,
from: &MuName,
to: &MuName,
exclude_node: &MuName
) -> bool {
// we cannot exclude start and end of the path
assert!(exclude_node != from && exclude_node != to);
if from == to {
true
} else {
// we are doing BFS
// visited nodes
let mut visited: LinkedHashSet<&MuName> = LinkedHashSet::new();
// work queue
let mut work_list: Vec<&MuName> = vec![];
// initialize visited nodes, and work queue
visited.insert(from);
work_list.push(from);
while !work_list.is_empty() {
let n = work_list.pop().unwrap();
for succ in self.get_succs(n) {
if succ == exclude_node {
// we are not going to follow a path with the excluded node
continue;
} else {
// if we are reaching destination, return true
if succ == to {
return true;
}
// push succ to work list so we will traverse them later
if !visited.contains(succ) {
visited.insert(succ);
work_list.push(succ);
}
}
}
}
false
}
}
}
/// MachineCFGNode represents a block in machine code control flow graph
#[derive(Clone, Debug)]
pub struct MachineCFGNode {
block: MuName,
preds: Vec<MuName>,
succs: Vec<MuName>
} }
...@@ -54,6 +54,7 @@ impl<'vm> Compiler<'vm> { ...@@ -54,6 +54,7 @@ impl<'vm> Compiler<'vm> {
/// compiles a certain function version /// compiles a certain function version
pub fn compile(&self, func: &mut MuFunctionVersion) { pub fn compile(&self, func: &mut MuFunctionVersion) {
info!(""); info!("");
info!("compilation_start {}", func.id());
info!("Start compiling {}", func); info!("Start compiling {}", func);
info!(""); info!("");
debug!("{:?}", func); debug!("{:?}", func);
...@@ -78,6 +79,7 @@ impl<'vm> Compiler<'vm> { ...@@ -78,6 +79,7 @@ impl<'vm> Compiler<'vm> {
// build exception table for this function // build exception table for this function
unimplemented!() unimplemented!()
} }
info!("compilation_end {}", func.id());
} }
} }
...@@ -111,6 +113,7 @@ impl Default for CompilerPolicy { ...@@ -111,6 +113,7 @@ impl Default for CompilerPolicy {
// compilation // compilation
passes.push(Box::new(backend::inst_sel::InstructionSelection::new())); passes.push(Box::new(backend::inst_sel::InstructionSelection::new()));
passes.push(Box::new(backend::mc_loopanalysis::MCLoopAnalysis::new()));
passes.push(Box::new(backend::reg_alloc::RegisterAllocation::new())); passes.push(Box::new(backend::reg_alloc::RegisterAllocation::new()));
// machine code level passes // machine code level passes
......
...@@ -324,14 +324,19 @@ impl CompilerPass for GenMovPhi { ...@@ -324,14 +324,19 @@ impl CompilerPass for GenMovPhi {
// move every from_arg to target_arg // move every from_arg to target_arg
let mut i = 0; let mut i = 0;
for arg in block_info.from_args.iter() { for arg in block_info.from_args.iter() {
let m = func.new_inst(Instruction { let ref target_arg = target_args[i];
hdr: MuEntityHeader::unnamed(vm.next_id()), // when a block branches to itself, it is possible that
value: Some(vec![target_args[i].clone()]), // arg is the same as target_arg
ops: vec![arg.clone()], if arg.as_value() != target_arg {
v: Instruction_::Move(0) let m = func.new_inst(Instruction {
}); hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![target_args[i].clone()]),
vec.push(m); ops: vec![arg.clone()],
v: Instruction_::Move(0)
});
vec.push(m);
}
i += 1; i += 1;
} }
......
...@@ -33,9 +33,76 @@ impl TreeGen { ...@@ -33,9 +33,76 @@ impl TreeGen {
} }
fn is_movable(inst: &Instruction) -> bool { fn is_movable(inst: &Instruction) -> bool {
!inst.has_side_effect() is_suitable_child(inst)
} }
/// is this instruction suitable to be tree child (we may find pattern for it)?
fn is_suitable_child(inst: &Instruction) -> bool {
use ast::inst::Instruction_::*;
match inst.v {
Return(_) |
ThreadExit |
Throw(_) |
TailCall(_) |
Branch1(_) |
Branch2 { .. } |
Watchpoint { .. } |
WPBranch { .. } |
Call { .. } |
CCall { .. } |
SwapStackExc { .. } |
SwapStackKill { .. } |
Switch { .. } |
ExnInstruction { .. } |
PrintHex(_) |
SetRetval(_) |
KillStack(_) |
CurrentStack |
SwapStackExpr { .. } |
CommonInst_Tr64IsFp(_) |
CommonInst_Tr64IsInt(_) |
CommonInst_Tr64IsRef(_) |
CommonInst_Tr64FromFp(_) |
CommonInst_Tr64FromInt(_) |
CommonInst_Tr64FromRef(_, _) |
CommonInst_Tr64ToFp(_) |
CommonInst_Tr64ToInt(_) |
CommonInst_Tr64ToRef(_) |
CommonInst_Tr64ToTag(_) |
ExprCall { .. } |
ExprCCall { .. } |
New(_) |
AllocA(_) |
NewHybrid(_, _) |
AllocAHybrid(_, _) |
NewStack(_) |
NewThread { .. } |
NewFrameCursor(_) |
Select { .. } |
Fence(_) |
CommonInst_SetThreadLocal(_) |
CommonInst_Pin(_) |
CommonInst_Unpin(_) |
CommonInst_GetAddr(_) |
CmpXchg { .. } |
AtomicRMW { .. } |
Store { .. } => false,
BinOp(_, _, _) | BinOpWithStatus(_, _, _, _) | CommonInst_GetThreadLocal | Move(_) => false,
CmpOp(_, _, _) |
ConvOp { .. } |
Load { .. } |
GetIRef(_) |
GetFieldIRef { .. } |
GetElementIRef { .. } |
ShiftIRef { .. } |
GetVarPartIRef { .. } => true
}
}
impl CompilerPass for TreeGen { impl CompilerPass for TreeGen {
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
self.name self.name
...@@ -51,8 +118,8 @@ impl CompilerPass for TreeGen { ...@@ -51,8 +118,8 @@ impl CompilerPass for TreeGen {
// then we replace the use of the SSA with the actual variable // then we replace the use of the SSA with the actual variable
// we are doing it in two steps // we are doing it in two steps
// 1. if we see an expression that generates an SSA which is used only once, we take out // 1. if we see an expression that generates an SSA which is used only once and used
// the expression node // in its next instruction, we take out the expression node
// 2. if we see an SSA that is used only once (and it is this place for sure), we replace it // 2. if we see an SSA that is used only once (and it is this place for sure), we replace it
// with the expression node // with the expression node
// because of SSA form, it is guaranteed to see 1 before 2 for SSA variables. // because of SSA form, it is guaranteed to see 1 before 2 for SSA variables.
...@@ -71,7 +138,8 @@ impl CompilerPass for TreeGen { ...@@ -71,7 +138,8 @@ impl CompilerPass for TreeGen {
trace!("check block {}", label); trace!("check block {}", label);
trace!(""); trace!("");
for mut node in body.into_iter() { for i in 0..body.len() {
let mut node = body[i].clone();
trace!("check inst: {}", node); trace!("check inst: {}", node);
match &mut node.v { match &mut node.v {
&mut TreeNode_::Instruction(ref mut inst) => { &mut TreeNode_::Instruction(ref mut inst) => {
...@@ -105,6 +173,7 @@ impl CompilerPass for TreeGen { ...@@ -105,6 +173,7 @@ impl CompilerPass for TreeGen {
// * it generates only one value // * it generates only one value
// * the value is used only once // * the value is used only once
// * the instruction is movable // * the instruction is movable
// * the value is used in the next instruction
trace!("check if we should fold the inst"); trace!("check if we should fold the inst");
if inst.value.is_some() { if inst.value.is_some() {
let left = inst.value.as_ref().unwrap(); let left = inst.value.as_ref().unwrap();
...@@ -112,11 +181,23 @@ impl CompilerPass for TreeGen { ...@@ -112,11 +181,23 @@ impl CompilerPass for TreeGen {
// if left is _one_ variable that is used once