use ast::ir::*;
use ast::ptr::*;
use ast::inst::*;
use vm::VM;
use compiler::CompilerPass;
use std::any::Any;
use std::sync::RwLock;
pub struct GenMovPhi {
name: &'static str,
}
impl GenMovPhi {
pub fn new() -> GenMovPhi {
GenMovPhi{name: "Generate Phi Moves"}
}
}
struct IntermediateBlockInfo {
blk_id: MuID,
target: MuID,
from_args : Vec
>
}
impl CompilerPass for GenMovPhi {
fn name(&self) -> &'static str {
self.name
}
fn as_any(&self) -> &Any {
self
}
fn visit_function(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
let mut f_content = func.content.take().unwrap();
let mut new_blocks_to_insert : Vec = vec![];
// iteratio blocks
for (blk_id, mut block) in f_content.blocks.iter_mut() {
trace!("block: {}", blk_id);
// old block content
let block_content = block.content.as_ref().unwrap().clone();
let mut new_body = vec![];
let mut i = 0;
let i_last = block_content.body.len() - 1;
for node in block_content.body.iter() {
// check if this is the last element
if i != i_last {
new_body.push(node.clone());
} else {
trace!("last instruction is {}", node);
let last_inst = node.clone();
match last_inst.v {
TreeNode_::Instruction(inst) => {
let ops = inst.ops.read().unwrap();
match inst.v {
Instruction_::Branch2{cond, true_dest, false_dest, true_prob} => {
let true_dest = process_dest(true_dest, &mut new_blocks_to_insert, &ops, vm);
let false_dest = process_dest(false_dest, &mut new_blocks_to_insert, &ops, vm);
let new_inst = func.new_inst(Instruction{
hdr: inst.hdr.clone(),
value: inst.value.clone(),
ops: RwLock::new(ops.to_vec()),
v: Instruction_::Branch2 {
cond: cond,
true_dest: true_dest,
false_dest: false_dest,
true_prob: true_prob
}
});
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::Call{data, resume} => {
let norm_dest = process_dest(resume.normal_dest, &mut new_blocks_to_insert, &ops, vm);
let exn_dest = process_dest(resume.exn_dest, &mut new_blocks_to_insert, &ops, vm);
let new_inst = func.new_inst(Instruction{
hdr: inst.hdr.clone(),
value: inst.value.clone(),
ops: RwLock::new(ops.to_vec()),
v: Instruction_::Call {
data: data.clone(),
resume: ResumptionData{
normal_dest: norm_dest,
exn_dest: exn_dest
}
}
});
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::CCall{data, resume} => {
let norm_dest = process_dest(resume.normal_dest, &mut new_blocks_to_insert, &ops, vm);
let exn_dest = process_dest(resume.exn_dest, &mut new_blocks_to_insert, &ops, vm);
let new_inst = func.new_inst(Instruction{
hdr: inst.hdr.clone(),
value: inst.value.clone(),
ops: RwLock::new(ops.to_vec()),
v: Instruction_::Call {
data: data.clone(),
resume: ResumptionData{
normal_dest: norm_dest,
exn_dest: exn_dest
}
}
});
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
},
Instruction_::Switch{cond, default, mut branches} => {
let default_dest = process_dest(default, &mut new_blocks_to_insert, &ops, vm);
let new_branches = branches.drain(..).map(|pair| {
let dest = process_dest(pair.1, &mut new_blocks_to_insert, &ops, vm);
(pair.0, dest)
}).collect();
let new_inst = func.new_inst(Instruction{
hdr: inst.hdr.clone(),
value: inst.value.clone(),
ops: RwLock::new(ops.to_vec()),
v: Instruction_::Switch {
cond: cond,
default: default_dest,
branches: new_branches
}
});
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::Watchpoint{..} => {
unimplemented!()
},
Instruction_::WPBranch{..} => {
unimplemented!()
},
Instruction_::SwapStack{..} => {
unimplemented!()
},
Instruction_::ExnInstruction{..} => {
unimplemented!()
},
_ => {
trace!("no rewrite");
new_body.push(node.clone())
}
}
}
_ => panic!("expect a terminal instruction")
}
}
i += 1;
}
block.content = Some(BlockContent{
args : block_content.args.to_vec(),
exn_arg : block_content.exn_arg.clone(),
body : new_body,
keepalives: block_content.keepalives.clone()
});
}
// insert new blocks here
for block_info in new_blocks_to_insert {
let block = {
let mut ret = Block::new(block_info.blk_id);
let target_id = block_info.target;
let name = format!("intermediate_block_{}_to_{}", block_info.blk_id, target_id);
vm.set_name(ret.as_entity(), name);
let target_block = f_content.get_block(target_id);
assert!(target_block.content.is_some());
let ref target_args = target_block.content.as_ref().unwrap().args;
ret.content = Some(BlockContent{
args: vec![],
exn_arg: None,
body: {
let mut vec = vec![];
// 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: RwLock::new(vec![arg.clone()]),
v: Instruction_::Move(0)
});
vec.push(m);
i += 1;
}
// branch to target
let b = func.new_inst(Instruction{
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: None,
ops: RwLock::new(vec![]),
v: Instruction_::Branch1(Destination{
target: target_id,
args: vec![]
})
});
vec.push(b);
vec
},
keepalives: None
});
trace!("inserting new intermediate block: {:?}", ret);
ret
};
f_content.blocks.insert(block.id(), block);
}
func.define(f_content);
}
}
fn process_dest(dest: Destination, blocks_to_insert: &mut Vec, ops: &Vec>, vm: &VM) -> Destination {
if dest.args.is_empty() {
dest
} else {
let target = dest.target;
let mut from_args = vec![];
for arg in dest.args.iter() {
let from_arg = match arg {
&DestArg::Normal(i) => ops[i].clone(),
&DestArg::Freshbound(_) => unimplemented!()
};
from_args.push(from_arg);
};
let new_blk_id = vm.next_id();
let dest = Destination {
target: new_blk_id,
args: vec![]
};
blocks_to_insert.push(IntermediateBlockInfo {
blk_id: new_blk_id,
target: target,
from_args: from_args
});
dest
}
}