To protect your data, the CISO officer has suggested users to enable GitLab 2FA as soon as possible.

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() {
let mut func_ver = func_vers.get(&func.cur_ver.unwrap()).unwrap().write().unwrap();