Commit 9b6dba4c authored by Isaac Gariano's avatar Isaac Gariano

Merge branch 'develop' into 'tree-gen-fix'

Develop

See merge request !47
parents ad8ea5f3 1dbd1d9b
Pipeline #1252 passed with stages
in 50 minutes and 59 seconds
......@@ -71,6 +71,12 @@ testmuc:test_cmp:
- *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:
......
......@@ -766,7 +766,7 @@ impl Instruction {
ret.push_str(" ");
}
}
ret.push_str("}}");
ret.push_str("}");
ret
}
......
......@@ -1287,7 +1287,7 @@ impl fmt::Display for Constant {
write!(f, "]")
}
&Constant::NullRef => write!(f, "NULL"),
&Constant::ExternSym(ref name) => write!(f, "EXTERN \\\"{}\\\"", name),
&Constant::ExternSym(ref name) => write!(f, "EXTERN \"{}\"", name),
&Constant::List(ref vec) => {
write!(f, "List(").unwrap();
......
......@@ -37,7 +37,6 @@ use std::usize;
use std::ops;
use std::collections::HashSet;
use std::sync::RwLock;
use compiler::backend::code_emission::{emit_mu_types, emit_mu_globals};
macro_rules! trace_emit {
($arg1:tt $($arg:tt)*) => {
......@@ -3642,9 +3641,6 @@ pub fn emit_context_with_reloc(
use std::path;
use std::io::prelude::*;
emit_mu_types("", vm);
emit_mu_globals("", vm);
debug!("---Emit VM Context---");
create_emit_directory(vm);
......
......@@ -4160,7 +4160,6 @@ pub fn emit_sym_table(vm: &VM) {
use std::collections::HashMap;
use compiler::backend::code_emission::{emit_mu_types, emit_mu_globals};
/// emit vm context for current session, considering relocation symbols/fields from the client
pub fn emit_context_with_reloc(
......@@ -4171,9 +4170,6 @@ pub fn emit_context_with_reloc(
use std::path;
use std::io::prelude::*;
emit_mu_types("", vm);
emit_mu_globals("", vm);
// creates emit directy, and file
debug!("---Emit VM Context---");
create_emit_directory(vm);
......@@ -4485,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
......@@ -4542,7 +4543,7 @@ pub fn spill_rewrite(
};
trace!(
"reg {} defined in Inst{} is replaced as {}",
val_reg,
val_reg.id(),
i,
temp
);
......
......@@ -2765,6 +2765,13 @@ impl<'a> InstructionSelection {
// MUL with one is the original value
trace!("emit mul-ireg-1");
self.emit_move_node_to_value(&res_tmp, op1, f_content, f_context, vm);
} else if self.match_ireg(op1) && self.match_iconst_p2(op2) {
// MUL with a constant that is a power of 2 can be done with shl
trace!("emit mul-ireg-p2");
let tmp_op1 = self.emit_ireg(op1, f_content, f_context, vm);
let shift = self.node_iconst_to_p2(op2);
self.backend.emit_mov_r_r(&res_tmp, &tmp_op1);
self.backend.emit_shl_r_imm8(&res_tmp, shift as i8);
} else if self.match_ireg_ex(op1) && self.match_iconst_zero(op2) {
// MUL with zero is zero
trace!("emit mul-iregex-0");
......@@ -2875,16 +2882,25 @@ impl<'a> InstructionSelection {
match op_size {
1 | 2 | 4 | 8 => {
self.emit_udiv(op1, op2, f_content, f_context, vm);
if self.match_iconst_p2(op2) {
// we can simply logic shift right
let shift = self.node_iconst_to_p2(op2);
// mov rax -> result
let res_size = vm.get_backend_type_size(res_tmp.ty.id());
match res_size {
8 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::RAX),
4 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::EAX),
2 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AX),
1 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AL),
_ => panic!("unexpected res for node {:?}", node)
let tmp_op1 = self.emit_ireg(op1, f_content, f_context, vm);
self.backend.emit_mov_r_r(&res_tmp, &tmp_op1);
self.backend.emit_shr_r_imm8(&res_tmp, shift as i8);
} else {
self.emit_udiv(op1, op2, f_content, f_context, vm);
// mov rax -> result
let res_size = vm.get_backend_type_size(res_tmp.ty.id());
match res_size {
8 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::RAX),
4 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::EAX),
2 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AX),
1 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AL),
_ => panic!("unexpected res for node {:?}", node)
}
}
}
16 => {
......@@ -2913,16 +2929,25 @@ impl<'a> InstructionSelection {
match op_size {
1 | 2 | 4 | 8 => {
self.emit_idiv(op1, op2, f_content, f_context, vm);
if self.match_iconst_p2(op2) {
// we can simply arithmetic shift right
let shift = self.node_iconst_to_p2(op2);
// mov rax -> result
let res_size = vm.get_backend_type_size(res_tmp.ty.id());
match res_size {
8 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::RAX),
4 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::EAX),
2 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AX),
1 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AL),
_ => panic!("unexpected res for node {:?}", node)
let tmp_op1 = self.emit_ireg(op1, f_content, f_context, vm);
self.backend.emit_mov_r_r(&res_tmp, &tmp_op1);
self.backend.emit_sar_r_imm8(&res_tmp, shift as i8);
} else {
self.emit_idiv(op1, op2, f_content, f_context, vm);
// mov rax -> result
let res_size = vm.get_backend_type_size(res_tmp.ty.id());
match res_size {
8 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::RAX),
4 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::EAX),
2 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AX),
1 => self.backend.emit_mov_r_r(&res_tmp, &x86_64::AL),
_ => panic!("unexpected res for node {:?}", node)
}
}
}
16 => {
......@@ -5569,6 +5594,33 @@ impl<'a> InstructionSelection {
}
}
/// matches an integer that is power of 2
fn match_iconst_p2(&mut self, op: &TreeNode) -> bool {
match op.v {
TreeNode_::Value(ref pv) if pv.is_const() => {
if pv.is_int_const() {
math::is_power_of_two(pv.extract_int_const().unwrap() as usize).is_some()
} else {
false
}
}
_ => false
}
}
fn node_iconst_to_p2(&mut self, op: &TreeNode) -> u8 {
match op.v {
TreeNode_::Value(ref pv) if pv.is_const() => {
if pv.is_int_const() {
math::is_power_of_two(pv.extract_int_const().unwrap() as usize).unwrap()
} else {
unreachable!()
}
}
_ => unreachable!()
}
}
/// matches a floatingpoint zero
fn match_fconst_zero(&mut self, op: &TreeNode) -> bool {
match op.v {
......
......@@ -24,9 +24,7 @@ use std::path;
use std::io::prelude::*;
use std::fs::File;
use std::collections::HashMap;
/// should emit Mu IR dot graph?
pub const EMIT_MUIR: bool = true;
pub use vm::uir_output::create_emit_directory;
/// should emit machien code dot graph?
pub const EMIT_MC_DOT: bool = true;
......@@ -62,15 +60,6 @@ impl CompilerPass for CodeEmission {
}
}
/// creates the emit directory (if it doesnt exist)
pub fn create_emit_directory(vm: &VM) {
use std::fs;
match fs::create_dir(&vm.vm_options.flag_aot_emit_dir) {
Ok(_) => {}
Err(_) => {}
}
}
/// creates an file to write, panics if the creation fails
fn create_emit_file(name: String, vm: &VM) -> File {
let mut file_path = path::PathBuf::new();
......@@ -89,89 +78,6 @@ fn create_emit_file(name: String, vm: &VM) -> File {
}
}
#[allow(dead_code)]
pub fn emit_mu_types(suffix: &str, vm: &VM) {
if EMIT_MUIR {
create_emit_directory(vm);
let mut file_path = path::PathBuf::new();
file_path.push(&vm.vm_options.flag_aot_emit_dir);
file_path.push("___types".to_string() + suffix + ".uir");
let mut file = match File::create(file_path.as_path()) {
Err(why) => {
panic!(
"couldn't create mu types file {}: {}",
file_path.to_str().unwrap(),
why
)
}
Ok(file) => file
};
{
use ast::types::*;
let ty_guard = vm.types().read().unwrap();
let struct_map = STRUCT_TAG_MAP.read().unwrap();
let hybrid_map = HYBRID_TAG_MAP.read().unwrap();
for ty in ty_guard.values() {
if ty.is_struct() {
write!(file, ".typedef {} = ", ty.hdr).unwrap();
let struct_ty = struct_map
.get(&ty.get_struct_hybrid_tag().unwrap())
.unwrap();
writeln!(file, "{}", struct_ty).unwrap();
writeln!(file, "\n\t/*{}*/", vm.get_backend_type_info(ty.id())).unwrap();
} else if ty.is_hybrid() {
write!(file, ".typedef {} = ", ty.hdr).unwrap();
let hybrid_ty = hybrid_map
.get(&ty.get_struct_hybrid_tag().unwrap())
.unwrap();
writeln!(file, "{}", hybrid_ty).unwrap();
writeln!(file, "\n\t/*{}*/", vm.get_backend_type_info(ty.id())).unwrap();
} else {
// we only care about struct
}
}
}
}
}
pub fn emit_mu_globals(suffix: &str, vm: &VM) {
if EMIT_MUIR {
create_emit_directory(vm);
let mut file_path = path::PathBuf::new();
file_path.push(&vm.vm_options.flag_aot_emit_dir);
file_path.push("___globals".to_string() + suffix + ".uir");
let mut file = match File::create(file_path.as_path()) {
Err(why) => {
panic!(
"couldn't create mu globals file {}: {}",
file_path.to_str().unwrap(),
why
)
}
Ok(file) => file
};
let global_guard = vm.globals().read().unwrap();
for g in global_guard.values() {
writeln!(
file,
".global {}<{}>",
g.name(),
g.ty.get_referent_ty().unwrap()
).unwrap();
}
}
}
fn emit_mc_dot(func: &MuFunctionVersion, vm: &VM) {
let func_name = func.name();
......
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.
......
......@@ -21,6 +21,14 @@ use utils::LinkedHashSet;
use utils::LinkedHashMap;
use std::fmt;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum NodeType {
Def,
Use,
Copy,
Machine
}
/// GraphNode represents a node in the interference graph.
#[derive(Clone, Copy, PartialEq)]
pub struct Node {
......@@ -120,7 +128,13 @@ impl InterferenceGraph {
/// creates a new node for a temp (if we already created a temp for the temp, returns the node)
/// This function will increase spill cost for the node by 1 each tiem it is called for the temp
fn new_node(&mut self, reg_id: MuID, context: &FunctionContext) -> MuID {
fn new_node(
&mut self,
reg_id: MuID,
ty: NodeType,
loop_depth: usize,
context: &FunctionContext
) -> MuID {
let entry = context.get_value(reg_id).unwrap();
// if it is the first time, create the node
......@@ -140,11 +154,26 @@ impl InterferenceGraph {
// get node
let node_mut = self.nodes.get_mut(&reg_id).unwrap();
// increase node spill cost
node_mut.spill_cost += 1.0f32;
node_mut.spill_cost += InterferenceGraph::spillcost_heuristic(ty, loop_depth);
reg_id
}
fn spillcost_heuristic(ty: NodeType, loop_depth: usize) -> f32 {
const DEF_WEIGHT: f32 = 1f32;
const USE_WEIGHT: f32 = 1f32;
const COPY_WEIGHT: f32 = 0.5f32;
let loop_depth = loop_depth as i32;
match ty {
NodeType::Machine => 0f32,
NodeType::Def => DEF_WEIGHT * (10f32.powi(loop_depth)),
NodeType::Use => USE_WEIGHT * (10f32.powi(loop_depth)),
NodeType::Copy => COPY_WEIGHT * (10f32.powi(loop_depth))
}
}
/// returns all the nodes in the graph
pub fn nodes(&self) -> Vec<MuID> {
self.nodes.keys().map(|x| *x).collect()
......@@ -205,7 +234,7 @@ impl InterferenceGraph {
};
if !self.adj_set.contains(&(u, v)) && u != v {
trace!("add edge ({}, {})", u, v);
trace!(" add edge ({}, {})", u, v);
self.adj_set.insert((u, v));
self.adj_set.insert((v, u));
......@@ -214,13 +243,13 @@ impl InterferenceGraph {
self.adj_list.get_mut(&u).unwrap().insert(v);
let degree = self.get_degree_of(u);
self.set_degree_of(u, degree + 1);
trace!("increase degree of {} to {}", u, degree + 1);
trace!(" increase degree of {} to {}", u, degree + 1);
}
if !is_precolored(v) {
self.adj_list.get_mut(&v).unwrap().insert(u);
let degree = self.get_degree_of(v);
self.set_degree_of(v, degree + 1);
trace!("increase degree of {} to {}", v, degree + 1);
trace!(" increase degree of {} to {}", v, degree + 1);
}
}
}
......@@ -281,7 +310,7 @@ impl InterferenceGraph {
}
pub fn set_degree_of(&mut self, reg: MuID, degree: usize) {
debug!("DEGREE({}) SET TO {}", reg, degree);
trace!(" (set degree({}) = {})", reg, degree);
self.degree.insert(reg, degree);
}
......@@ -327,6 +356,8 @@ pub fn build_interference_graph_chaitin_briggs(
cf: &mut CompiledFunction,
func: &MuFunctionVersion
) -> InterferenceGraph {
use compiler::backend::reg_alloc::graph_coloring::liveness::NodeType::*;
let _p = hprof::enter("regalloc: build global liveness");
build_global_liveness(cf, func);
drop(_p);
......@@ -339,22 +370,47 @@ pub fn build_interference_graph_chaitin_briggs(
// precolor machine register nodes
for reg in backend::all_regs().values() {
let reg_id = c(reg.extract_ssa_id().unwrap());
let node = ig.new_node(reg_id, &func.context);
let node = ig.new_node(reg_id, Machine, 0, &func.context);
let precolor = backend::get_color_for_precolored(reg_id);
ig.color_node(node, precolor);
}
// initialize and creates nodes for all the involved temps/regs
for i in 0..cf.mc().number_of_insts() {
for reg_id in cf.mc().get_inst_reg_defines(i) {
let reg_id = c(reg_id);
ig.new_node(reg_id, &func.context);
}
let mc = cf.mc();
for block in mc.get_all_blocks() {
debug!("build graph node for block {}", block);
let loop_depth: usize = match cf.loop_analysis.as_ref().unwrap().loop_depth.get(&block) {
Some(depth) => *depth,
None => 0
};
debug!("loop depth = {}", loop_depth);
for i in mc.get_block_range(&block).unwrap() {
// we separate the case of move nodes, and normal instruction
// as they yield different spill cost
// (we prefer spill a node in move instruction
// as the move instruction can be eliminated)
if mc.is_move(i) {
for reg_id in mc.get_inst_reg_defines(i) {
let reg_id = c(reg_id);
ig.new_node(reg_id, Copy, loop_depth, &func.context);
}
for reg_id in cf.mc().get_inst_reg_uses(i) {
let reg_id = c(reg_id);
ig.new_node(reg_id, &func.context);
for reg_id in mc.get_inst_reg_uses(i) {
let reg_id = c(reg_id);
ig.new_node(reg_id, Copy, loop_depth, &func.context);
}
} else {
for reg_id in mc.get_inst_reg_defines(i) {
let reg_id = c(reg_id);
ig.new_node(reg_id, Def, loop_depth, &func.context);
}
for reg_id in mc.get_inst_reg_uses(i) {
let reg_id = c(reg_id);
ig.new_node(reg_id, Use, loop_depth, &func.context);
}
}
}
}
......
......@@ -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