Commit a5af3d18 authored by qinsoon's avatar qinsoon

implement switch

parent 6e44ecc6
......@@ -192,6 +192,52 @@ impl <'a> InstructionSelection {
// jmp
self.backend.emit_jmp(target);
},
Instruction_::Switch{cond, ref default, ref branches} => {
let ops = inst.ops.read().unwrap();
let ref cond = ops[cond];
if self.match_ireg(cond) {
let tmp_cond = self.emit_ireg(cond, f_content, f_context, vm);
// emit each branch
for &(case_op_index, ref case_dest) in branches {
let ref case_op = ops[case_op_index];
// process dest
self.process_dest(&ops, case_dest, f_content, f_context, vm);
let target = f_content.get_block(case_dest.target).name().unwrap();
if self.match_iimm(case_op) {
let imm = self.node_iimm_to_i32(case_op);
// cmp case cond
self.backend.emit_cmp_imm32_r64(imm, &tmp_cond);
// je dest
self.backend.emit_je(target);
} else if self.match_ireg(case_op) {
let tmp_case_op = self.emit_ireg(case_op, f_content, f_context, vm);
// cmp case cond
self.backend.emit_cmp_r64_r64(&tmp_case_op, &tmp_cond);
// je dest
self.backend.emit_je(target);
} else {
panic!("expecting ireg cond to be either iimm or ireg: {}", cond);
}
}
// emit default
self.process_dest(&ops, default, f_content, f_context, vm);
let default_target = f_content.get_block(default.target).name().unwrap();
self.backend.emit_jmp(default_target);
} else {
panic!("expecting cond in switch to be ireg: {}", cond);
}
}
Instruction_::ExprCall{ref data, is_abort} => {
if is_abort {
......
......@@ -89,6 +89,29 @@ fn dfs(cur: MuID, stack: &mut Vec<MuID>, visited: &mut Vec<MuID>, func: &mut MuF
}
],
// switch
Switch{ref default, ref branches, ..} => {
const BRANCH_DEFAULT_PROB : f32 = 0.1;
let switch_prob = (1.0f32 - BRANCH_DEFAULT_PROB) / (branches.len() as f32);
let mut ret : Vec<BlockEdge> = branches.iter().map(|pair| BlockEdge {
target: pair.1.target,
kind: check_edge_kind(pair.1.target, stack),
is_exception: false,
probability: switch_prob
}).collect();
// default
ret.push(BlockEdge {
target: default.target,
kind: check_edge_kind(default.target, stack),
is_exception: false,
probability: BRANCH_DEFAULT_PROB
});
ret
}
// watchpoints
Watchpoint{ref id, ref disable_dest, ref resume} => {
let ref normal = resume.normal_dest;
......@@ -153,6 +176,7 @@ fn dfs(cur: MuID, stack: &mut Vec<MuID>, visited: &mut Vec<MuID>, func: &mut MuF
// call
Call{ref resume, ..}
| CCall{ref resume, ..}
| SwapStack{ref resume, ..}
| ExnInstruction{ref resume, ..} => {
let ref normal = resume.normal_dest;
......
......@@ -8,4 +8,5 @@ mod test_exception;
mod test_thread;
mod test_floatingpoint;
mod test_int;
mod test_binop;
\ No newline at end of file
mod test_binop;
mod test_controlflow;
\ No newline at end of file
extern crate mu;
extern crate log;
extern crate libloading;
use self::mu::ast::types::*;
use self::mu::ast::ir::*;
use self::mu::ast::inst::*;
use self::mu::ast::op::*;
use self::mu::vm::*;
use self::mu::testutil;
use std::sync::RwLock;
#[test]
fn test_switch() {
let lib = testutil::compile_fnc("switch", &switch);
unsafe {
let switch : libloading::Symbol<unsafe extern fn(u64) -> u64> = lib.get(b"switch").unwrap();
let res = switch(0);
println!("switch(0) = {}", res);
assert!(res == 0);
let res = switch(1);
println!("switch(1) = {}", res);
assert!(res == 1);
let res = switch(2);
println!("switch(2) = {}", res);
assert!(res == 2);
let res = switch(3);
println!("switch(3) = {}", res);
assert!(res == 99);
}
}
fn switch() -> VM {
let vm = VM::new();
// .typedef @int64 = int<64>
let type_def_int64 = vm.declare_type(vm.next_id(), MuType_::int(64));
vm.set_name(type_def_int64.as_entity(), Mu("int64"));
// .const @int64_0 <@int64> = 0
let const_int64_0 = vm.declare_const(vm.next_id(), type_def_int64.clone(), Constant::Int(0));
// .const @int64_1 <@int64> = 1
let const_int64_1 = vm.declare_const(vm.next_id(), type_def_int64.clone(), Constant::Int(1));
// .const @int64_2 <@int64> = 2
let const_int64_2 = vm.declare_const(vm.next_id(), type_def_int64.clone(), Constant::Int(2));
// .const @int64_99 <@int64> = 99
let const_int64_99 = vm.declare_const(vm.next_id(), type_def_int64.clone(), Constant::Int(99));
// .funcsig @switch_sig = (@int64) -> (@int64)
let switch_sig = vm.declare_func_sig(vm.next_id(), vec![type_def_int64.clone()], vec![type_def_int64.clone()]);
vm.set_name(switch_sig.as_entity(), Mu("switch_sig"));
// .funcdecl @switch <@switch_sig>
let func_id = vm.next_id();
let func = MuFunction::new(func_id, switch_sig.clone());
vm.set_name(func.as_entity(), Mu("switch"));
vm.declare_func(func);
// .funcdef @switch VERSION @switch_v1 <@switch_sig>
let mut func_ver = MuFunctionVersion::new(vm.next_id(), func_id, switch_sig.clone());
vm.set_name(func_ver.as_entity(), Mu("switch_v1"));
// %entry(<@int64> %a):
let mut blk_entry = Block::new(vm.next_id());
vm.set_name(blk_entry.as_entity(), Mu("entry"));
let blk_entry_a = func_ver.new_ssa(vm.next_id(), type_def_int64.clone());
vm.set_name(blk_entry_a.as_entity(), Mu("blk_entry_a"));
// SWITCH %a %blk_default (0 -> %blk_ret0, 1 -> %blk_ret1, 2 -> %blk_ret2)
let const0 = func_ver.new_constant(const_int64_0.clone());
let const1 = func_ver.new_constant(const_int64_1.clone());
let const2 = func_ver.new_constant(const_int64_2.clone());
let blk_default_id = vm.next_id();
let blk_ret0_id = vm.next_id();
let blk_ret1_id = vm.next_id();
let blk_ret2_id = vm.next_id();
let blk_entry_switch = func_ver.new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: None,
ops: RwLock::new(vec![
blk_entry_a.clone(), // 0
const0.clone(), // 1
const1.clone(), // 2
const2.clone(), // 3
]),
v: Instruction_::Switch {
cond: 0,
default: Destination {
target: blk_default_id,
args: vec![]
},
branches: vec![
(1, Destination{target: blk_ret0_id, args: vec![]}),
(2, Destination{target: blk_ret1_id, args: vec![]}),
(3, Destination{target: blk_ret2_id, args: vec![]})
]
}
});
blk_entry.content = Some(BlockContent{
args: vec![blk_entry_a.clone_value()],
exn_arg: None,
body: vec![blk_entry_switch],
keepalives: None
});
// blk_default
let mut blk_default = Block::new(blk_default_id);
vm.set_name(blk_default.as_entity(), Mu("default"));
let const99 = func_ver.new_constant(const_int64_99.clone());
let blk_default_ret = func_ver.new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: None,
ops: RwLock::new(vec![const99]),
v: Instruction_::Return(vec![0])
});
blk_default.content = Some(BlockContent{
args: vec![],
exn_arg: None,
body: vec![blk_default_ret],
keepalives: None
});
// blk_ret0
let mut blk_ret0 = Block::new(blk_ret0_id);
vm.set_name(blk_ret0.as_entity(), Mu("ret0"));
let blk_ret0_ret = func_ver.new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: None,
ops: RwLock::new(vec![const0.clone()]),
v: Instruction_::Return(vec![0])
});
blk_ret0.content = Some(BlockContent{
args: vec![],
exn_arg: None,
body: vec![blk_ret0_ret],
keepalives: None
});
// blk_ret1
let mut blk_ret1 = Block::new(blk_ret1_id);
vm.set_name(blk_ret1.as_entity(), Mu("ret1"));
let blk_ret1_ret = func_ver.new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: None,
ops: RwLock::new(vec![const1.clone()]),
v: Instruction_::Return(vec![0])
});
blk_ret1.content = Some(BlockContent{
args: vec![],
exn_arg: None,
body: vec![blk_ret1_ret],
keepalives: None
});
// blk_ret2
let mut blk_ret2 = Block::new(blk_ret2_id);
vm.set_name(blk_ret2.as_entity(), Mu("ret2"));
let blk_ret2_ret = func_ver.new_inst(Instruction {
hdr: MuEntityHeader::unnamed(vm.next_id()),
value: None,
ops: RwLock::new(vec![const2.clone()]),
v: Instruction_::Return(vec![0])
});
blk_ret2.content = Some(BlockContent{
args: vec![],
exn_arg: None,
body: vec![blk_ret2_ret],
keepalives: None
});
func_ver.define(FunctionContent{
entry: blk_entry.id(),
blocks: hashmap!{
blk_entry.id() => blk_entry,
blk_default_id => blk_default,
blk_ret0_id => blk_ret0,
blk_ret1_id => blk_ret1,
blk_ret2_id => blk_ret2
}
});
vm.define_func_version(func_ver);
vm
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment