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:
- *build_muc
- 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:
stage: test
script:
......
......@@ -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)
// FIXME: should deprecate this function, and get the length from BackendType
pub fn get_int_length(&self) -> Option<usize> {
......
......@@ -4743,15 +4743,12 @@ impl<'a> InstructionSelection {
let ref ops = inst.ops;
let ref func = ops[calldata.func];
let ref func_sig = match func.v {
TreeNode_::Value(ref pv) => {
let ty: &MuType = &pv.ty;
match ty.v {
MuType_::FuncRef(ref sig) | MuType_::UFuncPtr(ref sig) => sig,
_ => panic!("expected funcref/ptr type")
}
let func_sig = {
let t = func.ty();
match t.v {
MuType_::FuncRef(ref sig) | MuType_::UFuncPtr(ref sig) => sig.clone(),
_ => panic!("expected funcref/ufuncptr type")
}
_ => panic!("expected funcref/ptr type")
};
debug_assert!(func_sig.arg_tys.len() == calldata.args.len());
......
......@@ -1731,17 +1731,17 @@ impl ASMCodeGen {
let inst = inst.to_string() + &op_postfix(len);
trace!("emit: {} {} {}", inst, op1, op2);
let (mem, mut uses) = self.prepare_mem(op2, inst.len() + 1);
let (reg, id1, loc1) = self.prepare_reg(op1, inst.len() + 1 + mem.len() + 1);
let (reg, id, loc) = self.prepare_reg(op1, inst.len() + 1);
let (mem, mut uses) = self.prepare_mem(op2, inst.len() + 1 + reg.len() + 1);
if uses.contains_key(&id1) {
let mut locs = uses.get_mut(&id1).unwrap();
vec_utils::add_unique(locs, loc1.clone());
if uses.contains_key(&id) {
let mut locs = uses.get_mut(&id).unwrap();
vec_utils::add_unique(locs, loc.clone());
} 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)
}
......@@ -1832,10 +1832,7 @@ impl ASMCodeGen {
/// 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>) {
let len = match dest.ty.get_int_length() {
Some(n) if n == 64 | 32 | 16 | 8 => n,
_ => panic!("unimplemented int types: {}", dest.ty)
};
let len = check_op_len(dest);
let inst = inst.to_string() + &op_postfix(len);
trace!("emit: {} {}, {} -> {}", inst, src, dest, dest);
......@@ -2256,10 +2253,26 @@ impl ASMCodeGen {
)
}
/// emits an instruction (use 2 fpregs, define 1st fpreg)
fn internal_fp_binop_def_r_mem(&mut self, inst: &str, dest: Reg, src: Reg) {
/// 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: Mem) {
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)
......@@ -2523,6 +2536,10 @@ impl CodeGenerator for ASMCodeGen {
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>) {
self.internal_binop_no_def_r_r("test", op1, op2)
}
......@@ -4464,7 +4481,12 @@ pub fn spill_rewrite(
.clone_value();
// 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);
// generate a load
......@@ -4521,7 +4543,7 @@ pub fn spill_rewrite(
};
trace!(
"reg {} defined in Inst{} is replaced as {}",
val_reg,
val_reg.id(),
i,
temp
);
......
......@@ -61,7 +61,8 @@ pub trait CodeGenerator {
// comparison
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_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_imm_r(&mut self, op1: i32, op2: Reg);
......
This diff is collapsed.
......@@ -14,6 +14,8 @@
/// A instruction selection pass. Uses simple tree pattern matching.
pub mod inst_sel;
/// A Dominator Tree pass for machine code.
pub mod mc_loopanalysis;
/// A register allocation pass. Graph coloring.
pub mod reg_alloc;
/// A peephole optimization pass after register allocation.
......
......@@ -14,13 +14,15 @@
use ast::ir::*;
use ast::ptr::*;
use compiler;
use compiler::frame::*;
use compiler::backend::mc_loopanalysis::MCLoopAnalysisResult;
use runtime::ValueLocation;
use rodal;
use utils::Address;
use std::sync::Arc;
use utils::{LinkedHashMap, LinkedHashSet};
use runtime::resolve_symbol;
use rodal;
use std::sync::Arc;
use std;
use std::ops;
use std::collections::HashMap;
......@@ -52,7 +54,10 @@ pub struct CompiledFunction {
/// start location of this compiled function
pub start: ValueLocation,
/// 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);
unsafe impl rodal::Dump for CompiledFunction {
......@@ -90,7 +95,8 @@ impl CompiledFunction {
mc: Some(mc),
frame: frame,
start: start_loc,
end: end_loc
end: end_loc,
loop_analysis: None
}
}
......@@ -215,6 +221,15 @@ pub trait MachineCode {
fn get_all_blocks(&self) -> Vec<MuName>;
/// gets the entry block
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)
fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>>;
/// gets the block for a given index, returns an Option for the block
......@@ -240,4 +255,183 @@ pub trait MachineCode {
fn patch_frame_size(&mut self, size: usize);
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> {
/// compiles a certain function version
pub fn compile(&self, func: &mut MuFunctionVersion) {
info!("");
info!("compilation_start {}", func.id());
info!("Start compiling {}", func);
info!("");
debug!("{:?}", func);
......@@ -78,6 +79,7 @@ impl<'vm> Compiler<'vm> {
// build exception table for this function
unimplemented!()
}
info!("compilation_end {}", func.id());
}
}
......@@ -111,6 +113,7 @@ impl Default for CompilerPolicy {
// compilation
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()));
// machine code level passes
......
......@@ -324,14 +324,19 @@ impl CompilerPass for GenMovPhi {
// move every from_arg to target_arg
let mut i = 0;
for arg in block_info.from_args.iter() {
let m = func.new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![target_args[i].clone()]),
ops: vec![arg.clone()],
v: Instruction_::Move(0)
});
vec.push(m);
let ref target_arg = target_args[i];
// when a block branches to itself, it is possible that
// arg is the same as target_arg
if arg.as_value() != target_arg {
let m = func.new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: Some(vec![target_args[i].clone()]),
ops: vec![arg.clone()],
v: Instruction_::Move(0)
});
vec.push(m);
}
i += 1;
}
......
......@@ -33,9 +33,76 @@ impl TreeGen {
}
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 {
fn name(&self) -> &'static str {
self.name
......@@ -51,8 +118,8 @@ impl CompilerPass for TreeGen {
// then we replace the use of the SSA with the actual variable
// 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
// the expression node
// 1. if we see an expression that generates an SSA which is used only once and used
// 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
// with the expression node
// because of SSA form, it is guaranteed to see 1 before 2 for SSA variables.
......@@ -71,7 +138,8 @@ impl CompilerPass for TreeGen {
trace!("check block {}", label);
trace!("");
for mut node in body.into_iter() {
for i in 0..body.len() {
let mut node = body[i].clone();
trace!("check inst: {}", node);
match &mut node.v {
&mut TreeNode_::Instruction(ref mut inst) => {
......@@ -105,6 +173,7 @@ impl CompilerPass for TreeGen {
// * it generates only one value
// * the value is used only once
// * the instruction is movable
// * the value is used in the next instruction
trace!("check if we should fold the inst");
if inst.value.is_some() {
let left = inst.value.as_ref().unwrap();
......@@ -112,11 +181,23 @@ impl CompilerPass for TreeGen {
// if left is _one_ variable that is used once
// we can put the expression as a child node to its use
if left.len() == 1 {
let ref val_lhs = left[0];
let lhs = context
.get_value_mut(left[0].extract_ssa_id().unwrap())
.unwrap();
if lhs.use_count() == 1 {
if is_movable(&inst) {
let next_inst_uses_lhs = {
if i != body.len() - 1 {
let ref next_inst = body[i + 1].as_inst_ref();
next_inst
.ops
.iter()
.any(|x| x.as_value() == val_lhs)
} else {
false
}
};
if is_movable(&inst) && next_inst_uses_lhs {
// FIXME: should be able to move the inst here
lhs.assign_expr(inst.clone());
......@@ -124,7 +205,7 @@ impl CompilerPass for TreeGen {
trace!("");
continue;
} else {
trace!("no, not movable");
trace!("no, not movable or not used by next inst");
}
} else {
trace!("no, use count more than 1");
......
......@@ -60,11 +60,18 @@ pub const WORD_SIZE: ByteSize = 1 << LOG_POINTER_SIZE;
mod linked_hashmap;
/// linked hashset implementation based on LinkedHashMap
mod linked_hashset;
/// linked multi-key hashmap implementation
mod linked_multimap;
/// tree implementation
mod tree;
// re-export these data structures
pub use linked_hashmap::LinkedHashMap;
pub use linked_hashset::LinkedHashSet;
pub use linked_multimap::LinkedMultiMap;
pub use linked_multimap::LinkedRepeatableMultiMap;
pub use tree::Tree;
pub use self::doubly::DoublyLinkedList;
/// mem module:
......
......@@ -91,6 +91,22 @@ impl<K: Hash + Eq, S: BuildHasher> LinkedHashSet<K, S> {
self.0.contains_key(k)
}
/// retains elements that matches the predicate
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(&K) -> bool
{
let mut retain = vec![];
while let Some(x) = self.0.pop_front() {
if f(&x.0) {
retain.push(x);
}
}
for x in retain {
self.0.insert(x.0, x.1);
}
}
/// removes an element from the set, do nothing if the set does not contain the element
pub fn remove<Q: ?Sized>(&mut self, k: &Q)
where
......@@ -141,6 +157,25 @@ impl<K: Hash + Eq, S: BuildHasher> LinkedHashSet<K, S> {
}
}
impl<K: Hash + Eq> PartialEq for LinkedHashSet<K> {
fn eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
for ele in self.iter() {
if !other.contains(ele) {
return false;
}
}
true
}
fn ne(&self, other: &Self) -> bool {
!self.eq(other)
}
}
impl<K: Hash + Eq> Eq for LinkedHashSet<K> {}
impl<K: Hash + Eq + Clone> Clone for LinkedHashSet<K> {
fn clone(&self) -> Self {
LinkedHashSet(self.0.clone())
......
// Copyright 2017 The Australian National University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::collections::hash_map;
use std::hash::Hash;
use std::fmt;
use std::fmt::Debug;
use linked_hashmap::LinkedHashMap;
use linked_hashmap::{Iter, Keys};
use linked_hashset::LinkedHashSet;
pub struct LinkedMultiMap<K, V, S = hash_map::RandomState> {
inner: LinkedHashMap<K, LinkedHashSet<V, S>>
}
impl<K: Hash + Eq, V: Hash + Eq> LinkedMultiMap<K, V> {
pub fn new() -> LinkedMultiMap<K, V> {