Commit 7f1c3933 authored by Yi Lin's avatar Yi Lin

Merge branch 'defuse' into 'master'

freeze heuristics, slight change to tree gen

See merge request !51
parents 37c19daa bf9d1668
......@@ -22,7 +22,7 @@ use utils::vec_utils;
use std::fmt;
/// Instruction represents a Mu instruction
#[derive(Debug)] // this implements Clone and Display
#[derive(Debug, Clone)] // this implements Display
pub struct Instruction {
pub hdr: MuEntityHeader,
/// the values this instruction holds
......@@ -38,17 +38,6 @@ pub struct Instruction {
// Instruction implements MuEntity
impl_mu_entity!(Instruction);
impl Clone for Instruction {
fn clone(&self) -> Self {
Instruction {
hdr: self.hdr.clone(),
value: self.value.clone(),
ops: self.ops.clone(),
v: self.v.clone()
}
}
}
impl Instruction {
pub fn clone_with_id(&self, new_id: MuID) -> Instruction {
let mut clone = self.clone();
......
......@@ -298,8 +298,8 @@ impl MuFunctionVersion {
})
}
pub fn new_inst(&mut self, v: Instruction) -> Box<TreeNode> {
Box::new(TreeNode {
pub fn new_inst(&mut self, v: Instruction) -> P<TreeNode> {
P(TreeNode {
v: TreeNode_::Instruction(v)
})
}
......@@ -714,7 +714,7 @@ pub enum EdgeKind {
pub struct BlockContent {
pub args: Vec<P<Value>>,
pub exn_arg: Option<P<Value>>,
pub body: Vec<Box<TreeNode>>,
pub body: Vec<P<TreeNode>>,
pub keepalives: Option<Vec<P<Value>>>
}
......@@ -852,13 +852,6 @@ impl TreeNode {
})
}
/// creates an owned Instruction TreeNode
pub fn new_boxed_inst(v: Instruction) -> Box<TreeNode> {
Box::new(TreeNode {
v: TreeNode_::Instruction(v)
})
}
/// creates a sharable Value TreeNode
pub fn new_value(v: P<Value>) -> P<TreeNode> {
P(TreeNode {
......@@ -923,7 +916,7 @@ impl TreeNode {
}
/// consumes the TreeNode, returns the instruction in it (or None if it is not an instruction)
pub fn as_inst_ref(&self) -> &Instruction {
pub fn as_inst(&self) -> &Instruction {
match &self.v {
&TreeNode_::Instruction(ref inst) => inst,
_ => panic!("expected inst")
......@@ -1171,7 +1164,10 @@ pub struct SSAVarEntry {
expr: Option<Instruction>,
// some ssa vars (such as int128) needs to be split into smaller vars
split: Option<Vec<P<Value>>>
split: Option<Vec<P<Value>>>,
// which instruction defines this value
def: Option<P<TreeNode>>
}
impl SSAVarEntry {
......@@ -1180,7 +1176,8 @@ impl SSAVarEntry {
val: val,
use_count: ATOMIC_USIZE_INIT,
expr: None,
split: None
split: None,
def: None
};
ret.use_count.store(0, Ordering::SeqCst);
......@@ -1226,6 +1223,16 @@ impl SSAVarEntry {
pub fn get_split(&self) -> &Option<Vec<P<Value>>> {
&self.split
}
pub fn has_def(&self) -> bool {
self.def.is_some()
}
pub fn set_def(&mut self, d: P<TreeNode>) {
self.def = Some(d);
}
pub fn get_def(&self) -> &Option<P<TreeNode>> {
&self.def
}
}
impl fmt::Display for SSAVarEntry {
......
......@@ -194,6 +194,15 @@ impl MuType {
}
}
/// is this type an integer type of certain width
pub fn is_int_n(&self, n: usize) -> bool {
if let Some(width) = self.get_int_length() {
width == n
} else {
false
}
}
/// is this type a floating point type? (float/double)
pub fn is_fp(&self) -> bool {
match self.v {
......
......@@ -21,7 +21,6 @@ use compiler::backend::reg_alloc::graph_coloring;
use compiler::backend::reg_alloc::graph_coloring::liveness::InterferenceGraph;
use compiler::machine_code::CompiledFunction;
use vm::VM;
use utils::vec_utils;
use utils::LinkedHashSet;
use utils::LinkedHashMap;
use compiler::backend::reg_alloc::graph_coloring::liveness::Move;
......@@ -171,20 +170,6 @@ impl<'a> GraphColoring<'a> {
coloring.regalloc()
}
/// returns formatted string for an ID
fn display_id(&self, id: MuID) -> String {
self.func.context.get_temp_display(id)
}
/// returns formatted string for a move
fn display_move(&self, m: Move) -> String {
format!(
"Move: {} -> {}",
self.display_id(m.from),
self.display_id(m.to)
)
}
/// does coloring register allocation
fn regalloc(mut self) -> GraphColoring<'a> {
trace!("---InterenceGraph---");
......@@ -835,14 +820,40 @@ impl<'a> GraphColoring<'a> {
fn freeze(&mut self) {
// it is not empty (checked before)
let node = self.worklist_freeze.pop_front().unwrap();
let node = self.freeze_heuristics();
trace!("Freezing {}...", node);
trace!(" insert {} to worklistSimplify", node);
trace!(" move {} from worklistFreeze to worklistSimplify", node);
self.worklist_freeze.remove(&node);
self.worklist_simplify.insert(node);
self.freeze_moves(node);
}
fn freeze_heuristics(&mut self) -> MuID {
use std::f32;
// we try to freeze a node that appears less frequently
// we compute freeze cost based on all the moves related with this node
let mut candidate = None;
let mut candidate_cost = f32::MAX;
for &n in self.worklist_freeze.iter() {
// freeze_cost(n) = SUM ((spill_cost(src) + spill_cost(dst)) for m (mov src->dst)
// in movelist[n])
let mut freeze_cost = 0f32;
for m in self.get_movelist(n).iter() {
freeze_cost += self.ig.get_spill_cost(m.from);
freeze_cost += self.ig.get_spill_cost(m.to);
}
if freeze_cost < candidate_cost {
candidate = Some(n);
candidate_cost = freeze_cost;
}
}
assert!(candidate.is_some());
candidate.unwrap()
}
fn freeze_moves(&mut self, u: MuID) {
trace!(" freeze moves for {}", u);
for m in self.node_moves(u).iter() {
......
......@@ -15,7 +15,6 @@
use ast::ir::*;
use ast::ptr::*;
use vm::VM;
use compiler::CompilerPass;
use std::any::Any;
......@@ -41,26 +40,36 @@ impl CompilerPass for DefUse {
}
#[allow(unused_variables)]
fn start_block(&mut self, vm: &VM, func_context: &mut FunctionContext, block: &mut Block) {
fn start_block(&mut self, vm: &VM, func_context: &mut FunctionContext, block: &mut Block) {}
#[allow(unused_variables)]
fn finish_block(&mut self, vm: &VM, func_context: &mut FunctionContext, block: &mut Block) {
// if an SSA appears in keepalives, its use count increases
let ref mut keepalives = block.content.as_mut().unwrap().keepalives;
if keepalives.is_some() {
for op in keepalives.as_mut().unwrap().iter_mut() {
let ref keepalives = block.content.as_ref().unwrap().keepalives;
if let &Some(ref keepalives) = keepalives {
for op in keepalives.iter() {
use_value(op, func_context);
}
}
}
#[allow(unused_variables)]
fn visit_inst(&mut self, vm: &VM, func_context: &mut FunctionContext, node: &TreeNode) {
// if an SSA appears in operands of instrs, its use count increases
match node.v {
TreeNode_::Instruction(ref inst) => {
for op in inst.ops.iter() {
use_op(op, func_context);
}
fn visit_inst(&mut self, vm: &VM, func_context: &mut FunctionContext, node: &P<TreeNode>) {
let inst = node.as_inst();
if let &Some(ref vals) = &inst.value {
for val in vals {
let id = val.extract_ssa_id().unwrap();
func_context
.get_value_mut(id)
.unwrap()
.set_def(node.clone());
}
_ => panic!("expected instruction node in visit_inst()")
}
// if an SSA appears in operands of instrs, its use count increases
for op in inst.ops.iter() {
use_op(op, func_context);
}
}
......
......@@ -126,7 +126,7 @@ fn emit_muir_dot_inner(file: &mut File, f_name: MuName, f_content: &FunctionCont
write!(
file,
" {}\\l",
escape_string(format!("{}", inst.as_inst_ref()))
escape_string(format!("{}", inst.as_inst()))
).unwrap();
}
......
......@@ -77,14 +77,14 @@ impl CompilerPass for GenMovPhi {
trace!("last instruction is {}", node);
let last_inst = node.clone();
match last_inst.v {
TreeNode_::Instruction(inst) => {
TreeNode_::Instruction(ref inst) => {
let ref ops = inst.ops;
let inst_name = inst.name().clone();
match inst.v {
Instruction_::Branch2 {
cond,
true_dest,
false_dest,
ref true_dest,
ref false_dest,
true_prob
} => {
// check and insert intermediate blocks for true/false dest
......@@ -121,9 +121,12 @@ impl CompilerPass for GenMovPhi {
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::Call { data, resume } => {
Instruction_::Call {
ref data,
ref resume
} => {
let norm_dest = process_dest(
resume.normal_dest,
&resume.normal_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -131,7 +134,7 @@ impl CompilerPass for GenMovPhi {
"norm"
);
let exn_dest = process_dest(
resume.exn_dest,
&resume.exn_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -155,9 +158,12 @@ impl CompilerPass for GenMovPhi {
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::CCall { data, resume } => {
Instruction_::CCall {
ref data,
ref resume
} => {
let norm_dest = process_dest(
resume.normal_dest,
&resume.normal_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -165,7 +171,7 @@ impl CompilerPass for GenMovPhi {
"norm"
);
let exn_dest = process_dest(
resume.exn_dest,
&resume.exn_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -191,8 +197,8 @@ impl CompilerPass for GenMovPhi {
}
Instruction_::Switch {
cond,
default,
mut branches
ref default,
ref branches
} => {
let default_dest = process_dest(
default,
......@@ -204,10 +210,10 @@ impl CompilerPass for GenMovPhi {
);
let new_branches = branches
.drain(..)
.iter()
.map(|pair| {
let dest = process_dest(
pair.1,
&pair.1,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -237,11 +243,11 @@ impl CompilerPass for GenMovPhi {
Instruction_::SwapStackExc {
stack,
is_exception,
args,
resume
ref args,
ref resume
} => {
let norm_dest = process_dest(
resume.normal_dest,
&resume.normal_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -249,7 +255,7 @@ impl CompilerPass for GenMovPhi {
"norm"
);
let exn_dest = process_dest(
resume.exn_dest,
&resume.exn_dest,
&mut new_blocks_to_insert,
&ops,
vm,
......@@ -264,7 +270,7 @@ impl CompilerPass for GenMovPhi {
v: Instruction_::SwapStackExc {
stack: stack,
is_exception: is_exception,
args: args,
args: args.clone(),
resume: ResumptionData {
normal_dest: norm_dest,
exn_dest: exn_dest
......@@ -376,7 +382,7 @@ impl CompilerPass for GenMovPhi {
/// the intermediate block as destination. Otherwise, return
/// the original destination
fn process_dest(
dest: Destination,
dest: &Destination,
blocks_to_insert: &mut Vec<IntermediateBlockInfo>,
ops: &Vec<P<TreeNode>>,
vm: &VM,
......@@ -384,9 +390,9 @@ fn process_dest(
label: &str
) -> Destination {
if dest.args.is_empty() {
dest
dest.clone()
} else {
let target = dest.target;
let ref target = dest.target;
let mut from_args = vec![];
for arg in dest.args.iter() {
......
......@@ -182,7 +182,7 @@ impl Inlining {
trace!("inserting inlined function at {}", inst);
// from TreeNode into Inst (we do not need old TreeNode)
let inst = inst.into_inst().unwrap();
let inst = inst.as_inst();
// inline expansion starts here
......@@ -225,7 +225,7 @@ impl Inlining {
data.args.iter().map(|x| ops[*x].clone()).collect();
let arg_indices: Vec<OpIndex> = (0..arg_nodes.len()).collect();
let branch = TreeNode::new_boxed_inst(Instruction {
let branch = TreeNode::new_inst(Instruction {
hdr: inst.hdr.clone(),
value: None,
ops: arg_nodes.clone(),
......@@ -259,7 +259,7 @@ impl Inlining {
if inst.value.is_none() {
vec![]
} else {
inst.value.unwrap()
inst.value.as_ref().unwrap().clone()
}
},
exn_arg: None,
......@@ -305,7 +305,7 @@ impl Inlining {
.as_mut()
.unwrap()
.body
.push(TreeNode::new_boxed_inst(branch));
.push(TreeNode::new_inst(branch));
// next block
let mut next_block = resume.normal_dest.target.clone();
......@@ -348,7 +348,7 @@ impl Inlining {
}
},
exn_arg: None,
body: vec![TreeNode::new_boxed_inst(branch)],
body: vec![TreeNode::new_inst(branch)],
keepalives: None
});
......@@ -452,7 +452,7 @@ fn copy_inline_blocks(
for i in 0..old_block_content.body.len() - 1 {
block_content.body.push(match old_block_content.body[i].v {
TreeNode_::Instruction(ref inst) => {
TreeNode::new_boxed_inst(inst.clone_with_id(vm.next_id()))
TreeNode::new_inst(inst.clone_with_id(vm.next_id()))
}
_ => panic!("expect instruction as block body")
});
......@@ -464,7 +464,7 @@ fn copy_inline_blocks(
let inst_new_id = vm.next_id();
let last_inst_clone = match last_inst.v {
TreeNode_::Instruction(ref inst) => {
TreeNode::new_boxed_inst(inst.clone_with_id(inst_new_id))
TreeNode::new_inst(inst.clone_with_id(inst_new_id))
}
_ => panic!("expect instruction as block body")
};
......@@ -492,7 +492,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", branch);
block_content.body.push(TreeNode::new_boxed_inst(branch));
block_content.body.push(TreeNode::new_inst(branch));
}
// fix destination
......@@ -505,7 +505,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", branch);
block_content.body.push(TreeNode::new_boxed_inst(branch));
block_content.body.push(TreeNode::new_inst(branch));
}
&Instruction_::Branch2 {
ref cond,
......@@ -526,7 +526,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", branch2);
block_content.body.push(TreeNode::new_boxed_inst(branch2));
block_content.body.push(TreeNode::new_inst(branch2));
}
&Instruction_::Call {
ref data,
......@@ -543,7 +543,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", call);
block_content.body.push(TreeNode::new_boxed_inst(call));
block_content.body.push(TreeNode::new_inst(call));
}
&Instruction_::CCall {
ref data,
......@@ -560,7 +560,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", call);
block_content.body.push(TreeNode::new_boxed_inst(call));
block_content.body.push(TreeNode::new_inst(call));
}
&Instruction_::Switch {
ref cond,
......@@ -584,7 +584,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", switch);
block_content.body.push(TreeNode::new_boxed_inst(switch));
block_content.body.push(TreeNode::new_inst(switch));
}
&Instruction_::SwapStackExc {
......@@ -606,7 +606,7 @@ fn copy_inline_blocks(
};
trace!("rewrite to: {}", swapstack);
block_content.body.push(TreeNode::new_boxed_inst(swapstack));
block_content.body.push(TreeNode::new_inst(swapstack));
}
&Instruction_::Watchpoint { .. } |
&Instruction_::WPBranch { .. } |
......
......@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use ast::ptr::*;
use ast::ir::*;
use vm::VM;
use std::any::Any;
......@@ -26,8 +27,7 @@ pub use compiler::passes::inlining::Inlining;
mod ret_sink;
pub use compiler::passes::ret_sink::RetSink;
/// A Def-Use pass. Getting use info and count for SSA variables in the IR (we are not collecting
/// define info)
/// A Def-Use pass. Getting use info and count for SSA variables in the IR
mod def_use;
pub use compiler::passes::def_use::DefUse;
......@@ -112,5 +112,5 @@ pub trait CompilerPass {
fn start_block(&mut self, vm: &VM, func_context: &mut FunctionContext, block: &mut Block) {}
fn finish_block(&mut self, vm: &VM, func_context: &mut FunctionContext, block: &mut Block) {}
fn visit_inst(&mut self, vm: &VM, func_context: &mut FunctionContext, node: &TreeNode) {}
fn visit_inst(&mut self, vm: &VM, func_context: &mut FunctionContext, node: &P<TreeNode>) {}
}
......@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use ast::ptr::*;
use ast::ir::*;
use ast::inst::*;
......@@ -102,6 +103,15 @@ fn is_suitable_child(inst: &Instruction) -> bool {
}
}
fn should_always_move(inst: &Instruction) -> bool {
// for instructions that yields int<1>, we always move the instruction
if let Some(ref vals) = inst.value {
if vals.len() == 1 && vals[0].ty.is_int_n(1) {
return true;
}
}
false
}
impl CompilerPass for TreeGen {
fn name(&self) -> &'static str {
......@@ -129,100 +139,22 @@ impl CompilerPass for TreeGen {
let ref mut func_content = func.content;
let ref mut context = func.context;
for (label, ref mut block) in func_content.as_mut().unwrap().blocks.iter_mut() {
trace!("check block {}", label);
trace!("");
// take its content, we will need to put it back
let mut content = block.content.take().unwrap();
let body = content.body;
let mut new_body = vec![];
trace!("check block {}", label);
trace!("");
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) => {
// check whether any operands (SSA) can be replaced by expression
{
trace!("check if we can replace any operand with inst");
let ref mut ops = inst.ops;
for index in 0..ops.len() {
let possible_ssa_id = ops[index].extract_ssa_id();
if possible_ssa_id.is_some() {
let entry_value = context
.get_value_mut(possible_ssa_id.unwrap())
.unwrap();
if entry_value.has_expr() {
// replace the node with its expr
let expr = entry_value.take_expr();
trace!("{} replaced by {}", ops[index], expr);
ops[index] = TreeNode::new_inst(expr);
}
} else {
trace!("{} cant be replaced", ops[index]);
}
}
}
// check whether the instruction generates an SSA that is used only once
// An instruction can replace its value if
// * 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();
// 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 {
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());
trace!("yes");
trace!("");
continue;
} else {
trace!("no, not movable or not used by next inst");
}
} else {
trace!("no, use count more than 1");
}
} else {
trace!("no, yields more than 1 SSA var");
}
} else {
trace!("no, no value yielded");
}
}
_ => panic!("expected an instruction node here")
let node = body[i].clone();
let new_node = insert_inst_as_child(node, context);
if !is_child_inst(&new_node, i, &body, context) {
new_body.push(new_node);
}
trace!("add {} back to block {}", node, label);
trace!("");
new_body.push(node);
}
content.body = new_body;
......@@ -252,3 +184,92 @@ impl CompilerPass for TreeGen {
}
}
}
fn is_child_inst(
node: &P<TreeNode>,
cur_index: usize,
body: &Vec<P<TreeNode>>,
context: &mut FunctionContext
) -> bool {
trace!("is child inst: {}", node);
match node.v {
TreeNode_::Instruction(ref inst) => {
// check whether the instruction generates an SSA that is used only once
// An instruction can replace its value if
// * it generates only one value
// * the value is used only once
// * the instruction is movable
// * the value is used in the next instruction
if inst.value.is_some() {
let left = inst.value.as_ref().unwrap();
// 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 {
let next_inst_uses_lhs = {
if cur_index != body.len() - 1 {
let ref next_inst = body[cur_index + 1].as_inst();
next_inst.ops.iter().any(|x| x.as_value() == val_lhs)
} else {
false
}
};
if should_always_move(&inst) || (is_movable(&inst) && next_inst_uses_lhs) {
// FIXME: should be able to move the inst here
lhs.assign_expr(inst.clone());
trace!(" yes, extract the inst");
return true;
} else {
trace!(" no, not movable or not used by next inst");
}
} else {