WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.7% of users enabled 2FA.

Commit edcc327f authored by qinsoon's avatar qinsoon
Browse files

a pass GenMovPhi is added between TreeGen and CFA

introduced Move instruction at IR level.
Intermediate blocks will be inserted for multi-target branch
instructions
parent 61791d70
......@@ -266,7 +266,10 @@ pub enum Instruction_ {
// common inst
CommonInst_GetThreadLocal,
CommonInst_SetThreadLocal(OpIndex)
CommonInst_SetThreadLocal(OpIndex),
// internal use: mov from ops[0] to value
Move(OpIndex)
}
impl Instruction_ {
......@@ -379,8 +382,13 @@ impl Instruction_ {
&Instruction_::ExnInstruction{ref inner, ref resume} => {
format!("{} {}", inner.debug_str(ops), resume.debug_str(ops))
},
// common inst
&Instruction_::CommonInst_GetThreadLocal => format!("COMMONINST GetThreadLocal"),
&Instruction_::CommonInst_SetThreadLocal(op) => format!("COMMONINST SetThreadLocal {}", ops[op])
&Instruction_::CommonInst_SetThreadLocal(op) => format!("COMMONINST SetThreadLocal {}", ops[op]),
// move
&Instruction_::Move(from) => format!("MOVE {}", ops[from])
}
}
}
......
......@@ -28,7 +28,8 @@ pub fn is_terminal_inst(inst: &Instruction_) -> bool {
| &Select{..}
| &Fence(_)
| &CommonInst_GetThreadLocal
| &CommonInst_SetThreadLocal(_) => false,
| &CommonInst_SetThreadLocal(_)
| &Move(_) => false,
&Return(_)
| &ThreadExit
| &Throw(_)
......@@ -91,5 +92,6 @@ pub fn has_side_effect(inst: &Instruction_) -> bool {
&ExnInstruction{..} => true,
&CommonInst_GetThreadLocal => true,
&CommonInst_SetThreadLocal(_) => true,
&Move(_) => false,
}
}
......@@ -59,7 +59,9 @@ pub enum OpCode {
GetVarPartIRef,
CommonInst_GetThreadLocal,
CommonInst_SetThreadLocal
CommonInst_SetThreadLocal,
Move
}
pub fn pick_op_code_for_ssa(ty: &P<MuType>) -> OpCode {
......@@ -285,6 +287,7 @@ pub fn pick_op_code_for_inst(inst: &Instruction) -> OpCode {
Instruction_::Switch{..} => OpCode::Switch,
Instruction_::ExnInstruction{..} => OpCode::ExnInstruction,
Instruction_::CommonInst_GetThreadLocal => OpCode::CommonInst_GetThreadLocal,
Instruction_::CommonInst_SetThreadLocal(_) => OpCode::CommonInst_SetThreadLocal
Instruction_::CommonInst_SetThreadLocal(_) => OpCode::CommonInst_SetThreadLocal,
Instruction_::Move(_) => OpCode::Move,
}
}
......@@ -93,7 +93,7 @@ impl <'a> InstructionSelection {
let ref cond = ops[cond];
if self.match_cmp_res(cond) {
trace!("emit cmp_eq-branch2");
trace!("emit cmp_res-branch2");
match self.emit_cmp_res(cond, f_content, f_context, vm) {
op::CmpOp::EQ => {
if branch_if_true {
......@@ -1055,6 +1055,15 @@ impl <'a> InstructionSelection {
// store tmp_op -> [tl + USER_TLS_OFFSTE]
self.emit_store_base_offset(&tl, *thread::USER_TLS_OFFSET as i32, &tmp_op, vm);
}
Instruction_::Move(op) => {
let ops = inst.ops.read().unwrap();
let ref op = ops[op];
let tmp_res = self.get_result_value(node);
self.emit_move_node_to_value(&tmp_res, op, f_content, f_context, vm);
}
Instruction_::New(ref ty) => {
if cfg!(debug_assertions) {
......
......@@ -67,6 +67,7 @@ impl Default for CompilerPolicy {
// ir level passes
passes.push(Box::new(passes::DefUse::new()));
passes.push(Box::new(passes::TreeGen::new()));
passes.push(Box::new(passes::GenMovPhi::new()));
passes.push(Box::new(passes::ControlFlowAnalysis::new()));
passes.push(Box::new(passes::TraceGen::new()));
......
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<P<TreeNode>>
}
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<IntermediateBlockInfo> = vec![];
// iteratio blocks
for (blk_id, mut block) in f_content.blocks.iter_mut() {
trace!("block: {}", blk_id);
// old block content
let mut 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 mut ops = inst.ops.write().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<IntermediateBlockInfo>, ops: &Vec<P<TreeNode>>, 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
}
}
\ No newline at end of file
......@@ -5,11 +5,13 @@ mod def_use;
mod tree_gen;
mod control_flow;
mod trace_gen;
mod gen_mov_phi;
pub use compiler::passes::def_use::DefUse;
pub use compiler::passes::tree_gen::TreeGen;
pub use compiler::passes::control_flow::ControlFlowAnalysis;
pub use compiler::passes::trace_gen::TraceGen;
pub use compiler::passes::gen_mov_phi::GenMovPhi;
use std::any::Any;
......
......@@ -14,11 +14,13 @@ mod test_api;
mod common {
use std::fmt;
#[allow(dead_code)]
pub fn assert_vector_ordered <T: fmt::Debug> (left: &Vec<T>, right: &Vec<T>) {
assert_debug_str(left, right);
}
#[allow(dead_code)]
pub fn assert_vector_no_order <T: Ord + fmt::Debug + Clone> (left: &Vec<T>, right: &Vec<T>) {
let mut left_clone = left.clone();
left_clone.sort();
......@@ -27,7 +29,8 @@ mod common {
assert_debug_str(left_clone, right_clone);
}
#[allow(dead_code)]
pub fn assert_debug_str<T: fmt::Debug, U: fmt::Debug> (left: T, right: U) {
assert_eq!(format!("{:?}", left), format!("{:?}", right))
}
......
......@@ -54,6 +54,59 @@ fn test_build_tree() {
compiler.compile(&mut func_ver);
}
// consider one intermediate block
fn is_successor(from_id: MuID, to_id: MuID, content: &FunctionContent) -> bool {
let blk_from = content.get_block(from_id);
for outedge in blk_from.control_flow.succs.iter() {
if outedge.target == to_id {
return true;
}
let intermediate_block = content.get_block(outedge.target);
for int_outedge in intermediate_block.control_flow.succs.iter() {
if int_outedge.target == to_id {
return true;
}
}
}
return false;
}
fn has_successor(id: MuID, content: &FunctionContent) -> bool {
let blk = content.get_block(id);
!blk.control_flow.succs.is_empty()
}
fn is_predecessor(from_id: MuID, to_id: MuID, content: &FunctionContent) -> bool {
let blk_from = content.get_block(from_id);
for pred in blk_from.control_flow.preds.iter() {
if *pred == to_id {
return true;
}
let intermediate_block = content.get_block(*pred);
for int_pred in intermediate_block.control_flow.preds.iter() {
if *int_pred == to_id {
return true;
}
}
}
return false;
}
fn has_predecessor(id: MuID, content: &FunctionContent) -> bool {
let blk = content.get_block(id);
!blk.control_flow.preds.is_empty()
}
#[test]
fn test_cfa_factorial() {
VM::start_logging_trace();
......@@ -62,6 +115,7 @@ fn test_cfa_factorial() {
let compiler = Compiler::new(CompilerPolicy::new(vec![
Box::new(passes::DefUse::new()),
Box::new(passes::TreeGen::new()),
Box::new(passes::GenMovPhi::new()),
Box::new(passes::ControlFlowAnalysis::new())
]), vm.clone());
......@@ -75,23 +129,28 @@ fn test_cfa_factorial() {
// assert cfa
let content = func_ver.content.as_ref().unwrap();
// blk_0: preds=[], succs=[blk_2, blk_1]
let (blk_0_id, blk_1_id, blk_2_id) = (vm.id_of("blk_0"), vm.id_of("blk_1"), vm.id_of("blk_2"));
let blk_0 = content.get_block(blk_0_id);
assert_vector_no_order(&blk_0.control_flow.preds, &vec![]);
assert_vector_no_order(&block_edges_into_vec(&blk_0.control_flow.succs), &vec![blk_2_id, blk_1_id]);
// blk_0: preds=[], succs=[blk_2, blk_1] - however there will be intermediate block
// check blk_0 predecessor
assert!(!has_predecessor(blk_0_id, content));
// check blk_0 successor
assert!(is_successor(blk_0_id, blk_1_id, content));
assert!(is_successor(blk_0_id, blk_2_id, content));
// blk_2: preds=[blk_0, blk_1], succs=[]
let blk_2 = content.get_block(blk_2_id);
assert_vector_no_order(&blk_2.control_flow.preds, &vec![blk_0_id, blk_1_id]);
assert_vector_no_order(&block_edges_into_vec(&blk_2.control_flow.succs), &vec![]);
// check blk_2 predecessor
assert!(is_predecessor(blk_2_id, blk_0_id, content));
assert!(is_predecessor(blk_2_id, blk_1_id, content));
// check blk_2 successor
assert!(!has_successor(blk_2_id, content));
// blk_1: preds=[blk_0], succs=[blk_2]
let blk_1 = content.get_block(blk_1_id);
assert_vector_no_order(&blk_1.control_flow.preds, &vec![blk_0_id]);
assert_vector_no_order(&block_edges_into_vec(&blk_1.control_flow.succs), &vec![blk_2_id]);
// check blk_1 predecessor
assert!(is_predecessor(blk_1_id, blk_0_id, content));
// check blk_1 successor
assert!(is_successor(blk_1_id, blk_2_id, content));
}
#[test]
......@@ -102,6 +161,7 @@ fn test_cfa_sum() {
let compiler = Compiler::new(CompilerPolicy::new(vec![
Box::new(passes::DefUse::new()),
Box::new(passes::TreeGen::new()),
Box::new(passes::GenMovPhi::new()),
Box::new(passes::ControlFlowAnalysis::new())
]), vm.clone());
......@@ -116,32 +176,46 @@ fn test_cfa_sum() {
// assert cfa
let content = func_ver.content.as_ref().unwrap();
let entry_id = vm.id_of("entry");
let head_id = vm.id_of("head");
let ret_id = vm.id_of("ret");
let entry = vm.id_of("entry");
let head = vm.id_of("head");
let ret = vm.id_of("ret");
// entry: preds=[], succs=[head]
let entry = content.get_block(entry_id);
assert_vector_no_order(&entry.control_flow.preds, &vec![]);
assert_vector_no_order(&block_edges_into_vec(&entry.control_flow.succs), &vec![head_id]);
assert!(!has_predecessor(entry, content));
assert!(is_successor(entry, head, content));
// head: preds=[entry, head], succs=[head, ret]
let head = content.get_block(head_id);
assert_vector_no_order(&head.control_flow.preds, &vec![entry_id, head_id]);
assert_vector_no_order(&block_edges_into_vec(&head.control_flow.succs), &vec![ret_id, head_id]);
assert!(is_predecessor(head, entry, content));
assert!(is_predecessor(head, head, content));
assert!(is_successor(head, head, content));
assert!(is_successor(head, ret, content));
// ret: preds=[head], succs=[]
let ret = content.get_block(ret_id);
assert_vector_no_order(&ret.control_flow.preds, &vec![head_id]);
assert_vector_no_order(&block_edges_into_vec(&ret.control_flow.succs), &vec![]);
assert!(is_predecessor(ret, head, content));
assert!(!has_successor(ret, content));
}
fn block_edges_into_vec(edges: &Vec<BlockEdge>) -> Vec<MuID> {
let mut ret = vec![];
for edge in edges {
ret.push(edge.target);
// as long as expected appears in correct order in actual, it is correct
fn match_trace(actual: &Vec<MuID>, expected: &Vec<MuID>) -> bool {
assert!(actual.len() >= expected.len());
debug!("matching trace:");
debug!("actual: {:?}", actual);
debug!("expected: {:?}", expected);
let mut expected_cursor = 0;
for i in actual {
if *i == expected[expected_cursor] {
expected_cursor += 1;
if expected_cursor == expected.len() {
return true;
}
}
}
ret
return false;
}
#[test]
......@@ -152,6 +226,7 @@ fn test_trace_factorial() {
let compiler = Compiler::new(CompilerPolicy::new(vec![
Box::new(passes::DefUse::new()),
Box::new(passes::TreeGen::new()),
Box::new(passes::GenMovPhi::new()),
Box::new(passes::ControlFlowAnalysis::new()),
Box::new(passes::TraceGen::new())
]), vm.clone());
......@@ -163,8 +238,11 @@ fn test_trace_factorial() {