Commit 9d2cc163 authored by qinsoon's avatar qinsoon

[wip] working on instruction selection on factorial func

parent 66a09287
use ast::ptr::P;
use ast::types::*;
use ast::inst::*;
use ast::op::*;
use common::vector_as_str;
use std::collections::HashMap;
......@@ -21,7 +22,7 @@ pub struct MuFunction {
pub sig: P<MuFuncSig>,
pub content: Option<FunctionContent>,
pub context: FunctionContext,
pub block_trace: Option<Vec<MuTag>> // only available after Trace Generation Pass
}
......@@ -29,15 +30,16 @@ impl MuFunction {
pub fn new(fn_name: MuTag, sig: P<MuFuncSig>) -> MuFunction {
MuFunction{fn_name: fn_name, sig: sig, content: None, context: FunctionContext::new(), block_trace: None}
}
pub fn define(&mut self, content: FunctionContent) {
self.content = Some(content)
}
pub fn new_ssa(&mut self, id: MuID, tag: MuTag, ty: P<MuType>) -> P<TreeNode> {
self.context.values.insert(id, ValueEntry{id: id, tag: tag, ty: ty.clone(), use_count: Cell::new(0), expr: None});
P(TreeNode {
op: pick_op_code_for_ssa(&ty),
v: TreeNode_::Value(P(Value{
tag: tag,
ty: ty,
......@@ -45,22 +47,13 @@ impl MuFunction {
}))
})
}
pub fn new_constant(&mut self, tag: MuTag, ty: P<MuType>, v: Constant) -> P<TreeNode> {
P(TreeNode{
v: TreeNode_::Value(P(Value{
tag: tag,
ty: ty,
v: Value_::Constant(v)
}))
})
}
pub fn new_value(&mut self, v: P<Value>) -> P<TreeNode> {
pub fn new_constant(&mut self, v: P<Value>) -> P<TreeNode> {
P(TreeNode{
op: pick_op_code_for_const(&v.ty),
v: TreeNode_::Value(v)
})
}
}
}
#[derive(Debug)]
......@@ -73,11 +66,11 @@ impl FunctionContent {
pub fn get_entry_block(&self) -> &Block {
self.get_block(self.entry)
}
pub fn get_entry_block_mut(&mut self) -> &mut Block {
self.get_block_mut(self.entry)
}
pub fn get_block(&self, tag: MuTag) -> &Block {
let ret = self.blocks.get(tag);
match ret {
......@@ -85,14 +78,14 @@ impl FunctionContent {
None => panic!("cannot find block {}", tag)
}
}
pub fn get_block_mut(&mut self, tag: MuTag) -> &mut Block {
let ret = self.blocks.get_mut(tag);
match ret {
Some(b) => b,
None => panic!("cannot find block {}", tag)
}
}
}
}
#[derive(Debug)]
......@@ -106,11 +99,11 @@ impl FunctionContext {
values: HashMap::new()
}
}
pub fn get_value(&self, id: MuID) -> Option<&ValueEntry> {
self.values.get(&id)
}
pub fn get_value_mut(&mut self, id: MuID) -> Option<&mut ValueEntry> {
self.values.get_mut(&id)
}
......@@ -142,14 +135,14 @@ impl ControlFlow {
} else {
let mut hot_blk = self.succs[0].target;
let mut hot_prob = self.succs[0].probability;
for edge in self.succs.iter() {
if edge.probability > hot_prob {
hot_blk = edge.target;
hot_prob = edge.probability;
}
}
Some(hot_blk)
}
}
......@@ -191,21 +184,21 @@ pub enum EdgeKind {
pub struct BlockContent {
pub args: Vec<P<TreeNode>>,
pub body: Vec<P<TreeNode>>,
pub keepalives: Option<Vec<P<TreeNode>>>
pub keepalives: Option<Vec<P<TreeNode>>>
}
#[derive(Debug, Clone)]
/// always use with P<TreeNode>
pub struct TreeNode {
// pub op: OpCode,
pub v: TreeNode_
pub op: OpCode,
pub v: TreeNode_,
}
impl TreeNode {
impl TreeNode {
pub fn new_inst(v: Instruction) -> P<TreeNode> {
P(TreeNode{v: TreeNode_::Instruction(v)})
P(TreeNode{op: pick_op_code_for_inst(&v), v: TreeNode_::Instruction(v)})
}
pub fn extract_ssa_id(&self) -> Option<MuID> {
match self.v {
TreeNode_::Value(ref pv) => {
......@@ -229,7 +222,7 @@ impl fmt::Display for TreeNode {
write!(f, "+({} %{}#{})", pv.ty, pv.tag, id)
},
Value_::Constant(ref c) => {
write!(f, "+({} {})", pv.ty, c)
write!(f, "+({} {})", pv.ty, c)
}
}
},
......@@ -265,11 +258,11 @@ pub struct ValueEntry {
pub id: MuID,
pub tag: MuTag,
pub ty: P<MuType>,
// how many times this entry is used
// availalbe after DefUse pass
pub use_count: Cell<usize>,
// this field is only used during TreeGeneration pass
pub expr: Option<Instruction>
}
......@@ -294,7 +287,7 @@ pub enum Constant {
IRef(Address),
FuncRef(Address),
UFuncRef(Address),
Vector(Vec<Constant>),
Vector(Vec<Constant>),
}
impl fmt::Display for Constant {
......@@ -330,4 +323,4 @@ pub fn op_vector_str(vec: &Vec<OpIndex>, ops: &Vec<P<TreeNode>>) -> String {
}
}
ret
}
\ No newline at end of file
}
use ast::ptr::P;
use ast::types::*;
use ast::inst::*;
#[derive(Copy, Clone, Debug)]
pub enum OpCode {
// SSA
RegI64,
RegFP,
// Constant
IntImmI64,
FPImm,
// non-terminal
Assign,
Fence,
//terminal
Return,
ThreadExit,
......@@ -25,15 +29,16 @@ pub enum OpCode {
SwapStack,
Switch,
ExnInstruction,
// expression
BinOp,
CmpOp,
Binary(BinOp),
Comparison(CmpOp),
AtomicRMW(AtomicRMWOp),
ExprCall,
Load,
Store,
CmpXchg,
AtomicRMWOp,
New,
AllocA,
NewHybrid,
......@@ -49,6 +54,64 @@ pub enum OpCode {
GetVarPartIRef
}
pub fn pick_op_code_for_ssa(ty: &P<MuType>) -> OpCode {
use ast::types::MuType_::*;
let a : &MuType_ = ty;
match a {
// currently use i64 for all ints
&Int(_) => OpCode::RegI64,
// currently do not differentiate float and double
&Float
| &Double => OpCode::RegFP,
// ref and pointer types use RegI64
&Ref(_)
| &IRef(_)
| &WeakRef(_)
| &UPtr(_)
| &ThreadRef
| &StackRef
| &Tagref64
| &FuncRef(_)
| &UFuncPtr(_) => OpCode::RegI64,
// we are not supposed to have these as SSA
&Struct(_)
| &Array(_, _)
| &Hybrid(_, _)
| &Void => panic!("Not expecting {} as SSA", ty),
// unimplemented
&Vector(_, _) => unimplemented!()
}
}
pub fn pick_op_code_for_const(ty: &P<MuType>) -> OpCode {
use ast::types::MuType_::*;
let a : &MuType_ = ty;
match a {
// currently use i64 for all ints
&Int(_) => OpCode::IntImmI64,
// currently do not differentiate float and double
&Float
| &Double => OpCode::FPImm,
// ref and pointer types use RegI64
&Ref(_)
| &IRef(_)
| &WeakRef(_)
| &UPtr(_)
| &ThreadRef
| &StackRef
| &Tagref64
| &FuncRef(_)
| &UFuncPtr(_) => OpCode::IntImmI64,
// we are not supposed to have these as SSA
&Struct(_)
| &Array(_, _)
| &Hybrid(_, _)
| &Void => unimplemented!(),
// unimplemented
&Vector(_, _) => unimplemented!()
}
}
#[derive(Copy, Clone, Debug)]
pub enum BinOp {
// Int(n) BinOp Int(n) -> Int(n)
......@@ -61,14 +124,14 @@ pub enum BinOp {
And,
Or,
Xor,
// Int(n) BinOp Int(m) -> Int(n)
Shl,
Lshr,
AsHR,
Ashr,
// FP BinOp FP -> FP
Fadd,
FAdd,
FSub,
FMul,
FDiv,
......@@ -88,7 +151,7 @@ pub enum CmpOp {
UGT,
ULE,
ULT,
// for FP comparison
FFALSE,
FTRUE,
......@@ -121,4 +184,42 @@ pub enum AtomicRMWOp {
MIN,
UMAX,
UMIN
}
\ No newline at end of file
}
pub fn pick_op_code_for_inst(inst: &Instruction) -> OpCode {
match inst.v {
Instruction_::BinOp(op, _, _) => OpCode::Binary(op),
Instruction_::CmpOp(op, _, _) => OpCode::Comparison(op),
Instruction_::AtomicRMW{op, ..} => OpCode::AtomicRMW(op),
Instruction_::ExprCall{..} => OpCode::ExprCall,
Instruction_::Load{..} => OpCode::Load,
Instruction_::Store{..} => OpCode::Store,
Instruction_::CmpXchg{..} => OpCode::CmpXchg,
Instruction_::New(_) => OpCode::New,
Instruction_::AllocA(_) => OpCode::AllocA,
Instruction_::NewHybrid(_, _) => OpCode::NewHybrid,
Instruction_::AllocAHybrid(_, _) => OpCode::AllocAHybrid,
Instruction_::NewStack(_) => OpCode::NewStack,
Instruction_::NewThread(_, _) => OpCode::NewThread,
Instruction_::NewThreadExn(_, _) => OpCode::NewThreadExn,
Instruction_::NewFrameCursor(_) => OpCode::NewFrameCursor,
Instruction_::GetIRef(_) => OpCode::GetIRef,
Instruction_::GetFieldIRef{..} => OpCode::GetFieldIRef,
Instruction_::GetElementIRef{..} => OpCode::GetElementIRef,
Instruction_::ShiftIRef{..} => OpCode::ShiftIRef,
Instruction_::GetVarPartIRef{..} => OpCode::GetVarPartIRef,
Instruction_::Fence(_) => OpCode::Fence,
Instruction_::Return(_) => OpCode::Return,
Instruction_::ThreadExit => OpCode::ThreadExit,
Instruction_::Throw(_) => OpCode::Throw,
Instruction_::TailCall(_) => OpCode::TailCall,
Instruction_::Branch1(_) => OpCode::Branch1,
Instruction_::Branch2{..} => OpCode::Branch2,
Instruction_::Watchpoint{..} => OpCode::Watchpoint,
Instruction_::WPBranch{..} => OpCode::WPBranch,
Instruction_::Call{..} => OpCode::Call,
Instruction_::SwapStack{..} => OpCode::SwapStack,
Instruction_::Switch{..} => OpCode::Switch,
Instruction_::ExnInstruction{..} => OpCode::ExnInstruction
}
}
use ast::ir::*;
use ast::ptr::*;
use ast::inst::Destination;
use ast::inst::DestArg;
use ast::inst::Instruction_::*;
use ast::op;
use ast::op::OpCode;
use vm::context::VMContext;
use compiler::CompilerPass;
......@@ -19,18 +23,19 @@ impl CompilerPass for InstructionSelection {
fn name(&self) -> &'static str {
self.name
}
#[allow(unused_variables)]
fn start_function(&mut self, vm_context: &VMContext, func: &mut MuFunction) {
debug!("{}", self.name());
}
#[allow(unused_variables)]
fn visit_function(&mut self, vm_context: &VMContext, func: &mut MuFunction) {
for block_label in func.block_trace.as_ref().unwrap() {
let block = func.content.as_mut().unwrap().get_block_mut(block_label);
let block_content = block.content.as_mut().unwrap();
for inst in block_content.body.iter_mut() {
instruction_select(inst);
}
......@@ -38,33 +43,186 @@ impl CompilerPass for InstructionSelection {
}
}
#[allow(unused_variables)]
fn instruction_select(inst: &mut P<TreeNode>) {
trace!("instsel on node {}", inst);
match inst.v {
TreeNode_::Instruction(ref inst) => {
match inst.v {
Branch2{cond, ref true_dest, ref false_dest, true_prob} => {
let ref cond = inst.ops.borrow()[cond];
let (fallthrough_dest, branch_dest) = {
if true_prob > 0.5f32 {
(true_dest, false_dest)
} else {
(false_dest, true_dest)
}
};
match cond.v {
TreeNode_::Instruction(ref inst) => {
match inst.v {
CmpOp(op, op1, op2) => {
},
let mut ops = inst.ops.borrow_mut();
process_dest(&mut ops, fallthrough_dest);
process_dest(&mut ops, branch_dest);
let ref mut cond = ops[cond];
match cond.op {
OpCode::Comparison(op) => {
trace!("Tile comp-branch2");
match cond.v {
TreeNode_::Instruction(ref inst) => {
match inst.v {
CmpOp(op, op1, op2) => {
// cmp op1 op2
// jcc branch_dest
// #fallthrough_dest:
// ..
},
_ => panic!("expected a comparison op")
}
},
_ => panic!("unexpected inst as child node of branch2: {}", cond)
_ => panic!("expected a comparison inst")
}
},
TreeNode_::Value(ref pv) => {
OpCode::RegI64 | OpCode::IntImmI64 => {
trace!("Tile value-branch2");
// test/cmp pv 0
// jcc branch_dest
// #fallthrough_dest:
// ...
},
_ => {
trace!("nested: compute cond");
// instsel for cond first
instruction_select(cond);
// test/cmp res 0
// jcc branch_dest
// #fallthrough_dest:
// ...
trace!("Tile value-branch2 after computing cond")
}
}
},
Branch1(ref dest) => {
let mut ops = inst.ops.borrow_mut();
process_dest(&mut ops, dest);
trace!("Tile branch1");
// jmp
},
ExprCall{ref data, is_abort} => {
trace!("Tile exprcall");
let mut ops = inst.ops.borrow_mut();
for arg_index in data.args.iter() {
let ref mut arg = ops[*arg_index];
trace!("arg {}", arg);
match arg.op {
OpCode::RegI64 | OpCode::IntImmI64 => {
trace!("Tile move-gpr-arg");
// move to register
},
OpCode::RegFP | OpCode::FPImm => {
trace!("Tile move-fpr-arg");
// move to fp register
},
_ => {
trace!("nested: compute arg");
// instself for arg first
instruction_select(arg);
// mov based on type
trace!("Tile move-arg after computing arg");
}
}
}
},
Return(ref vals) => {
let mut ops = inst.ops.borrow_mut();
for val_index in vals.iter() {
let ref mut val = ops[*val_index];
trace!("return val: {}", val);
match val.op {
OpCode::RegI64 | OpCode::IntImmI64 => {
trace!("Tile move-gpr-ret");
// move to return register
}
OpCode::RegFP | OpCode::FPImm => {
trace!("Tile move-fpr-ret");
// move to return fp register
}
_ => {
trace!("nested: compute return val");
// instsel for return val first
instruction_select(val);
// move based on type
trace!("Tile move-ret-val after computing arg");
}
}
}
},
BinOp(op, op1, op2) => {
match op {
op::BinOp::Add => {
trace!("Tile add");
// mov op1, res
// add op2 res
},
op::BinOp::Sub => {
trace!("Tile sub")
// mov op1, res
// sub op1, res
},
op::BinOp::Mul => {
trace!("Tile mul")
// mov op1 rax
// mul op2 rax
// mov rax res
},
_ => unimplemented!()
}
}
_ => unimplemented!()
}
} // main switch
},
_ => panic!("expected instruction")
}
}
\ No newline at end of file
}
#[allow(unused_variables)]
fn process_dest(ops: &mut Vec<P<TreeNode>>, dest: &Destination) {
for dest_arg in dest.args.iter() {
match dest_arg {
&DestArg::Normal(op_index) => {
let ref mut arg = ops[op_index];
match arg.op {
OpCode::RegI64
| OpCode::RegFP
| OpCode::IntImmI64
| OpCode::FPImm => {
// do nothing
},
_ => {
trace!("nested: compute arg for branch");
// nested: compute arg
instruction_select(arg);
}
}
},
&DestArg::Freshbound(_) => unimplemented!()
}
}
}
\ No newline at end of file
......@@ -32,6 +32,7 @@ impl CompilerPolicy {
passes.push(Box::new(passes::DefUse::new()));
passes.push(Box::new(passes::TreeGen::new()));
passes.push(Box::new(passes::ControlFlowAnalysis::new()));
passes.push(Box::new(passes::TraceGen::new()));
passes.push(Box::new(backend::inst_sel::InstructionSelection::new()));
CompilerPolicy{passes: passes}
......
......@@ -24,28 +24,28 @@ fn test_sum() {
pub fn sum() -> VMContext {
let mut vm = VMContext::new();
// .typedef @int_64 = int<64>
let type_def_int64 = vm.declare_type("int_64", P(MuType::int(64)));
let type_def_int1 = vm.declare_type("int_1", P(MuType::int(1)));
// .const @int_64_0 <@int_64> = 0
// .const @int_64_1 <@int_64> = 1
let const_def_int64_0 = vm.declare_const("int64_0", type_def_int64.clone(), Constant::Int(0));
let const_def_int64_1 = vm.declare_const("int64_1", type_def_int64.clone(), Constant::Int(1));
// .funcsig @sum_sig = (@int_64) -> (@int_64)
let sum_sig = vm.declare_func_sig("sum_sig", vec![type_def_int64.clone()], vec![type_def_int64.clone()]);
// .funcdef @sum VERSION @sum_v1 <@sum_sig>
let mut func = MuFunction::new("sum", sum_sig.clone());
// %entry(<@int_64> %n):
let mut blk_entry = Block::new("entry");
let blk_entry_n = func.new_ssa(0, "blk_entry_n", type_def_int64.clone());
let const_def_int64_0_local = func.new_value(const_def_int64_0.clone()); // FIXME: why we need a local version?
let const_def_int64_1_local = func.new_value(const_def_int64_1.clone());
let const_def_int64_0_local = func.new_constant(const_def_int64_0.clone()); // FIXME: why we need a local version?
let const_def_int64_1_local = func.new_constant(const_def_int64_1.clone());
// BRANCH %head
let blk_entry_term = TreeNode::new_inst(Instruction {
value: None,
......@@ -55,20 +55,20 @@ pub fn sum() -> VMContext {
args: vec![DestArg::Normal(0), DestArg::Normal(1), DestArg::Normal(2)]
})
});
let blk_entry_content = BlockContent {
args: vec![blk_entry_n.clone()],
body: vec![blk_entry_term],
keepalives: None
};
blk_entry.content = Some(blk_entry_content);
// %head(<@int_64> %n, <@int_64> %s, <@int_64> %i):
let mut blk_head = Block::new("head");
let blk_head_n = func.new_ssa(1, "blk_head_n", type_def_int64.clone());
let blk_head_s = func.new_ssa(2, "blk_head_s", type_def_int64.clone());
let blk_head_i = func.new_ssa(3, "blk_head_i", type_def_int64.clone());
// %s2 = ADD %s %i
let blk_head_s2 = func.new_ssa(4, "blk_head_s2", type_def_int64.clone());
let blk_head_inst0 = TreeNode::new_inst(Instruction {
......@@ -76,7 +76,7 @@ pub fn sum() -> VMContext {
ops: RefCell::new(vec![blk_head_s.clone(), blk_head_i.clone()]),
v: Instruction_::BinOp(BinOp::Add, 0, 1)
});
// %i2 = ADD %i 1
let blk_head_i2 = func.new_ssa(5, "blk_head_i2", type_def_int64.clone());
let blk_head_inst1 = TreeNode::new_inst(Instruction {
......@@ -84,7 +84,7 @@ pub fn sum() -> VMContext {
ops: RefCell::new(vec![blk_head_i.clone(), const_def_int64_1_local.clone()]),
v: Instruction_::BinOp(BinOp::Add, 0, 1)
});
// %cond = UGT %i %n
let blk_head_cond = func.new_ssa(6, "blk_head_cond", type_def_int1.clone());
let blk_head_inst2 = TreeNode::new_inst(Instruction {
......@@ -117,18 +117,18 @@ pub fn sum() -> VMContext {
keepalives: None
};
blk_head.content = Some(blk_head_content);
// %ret(<@int_64> %s):
let mut blk_ret = Block::new("ret");
let blk_ret_s = func.new_ssa(7, "blk_ret_s", type_def_int64.clone());
// RET %s
let blk_ret_term = TreeNode::new_inst(Instruction{
value: None,
ops: RefCell::new(vec![blk_ret_s.clone()]),
v: Instruction_::Return(vec![0])
});
let blk_ret_content = BlockContent {
args: vec![blk_ret_s.clone()],
body: vec![blk_ret_term],
......@@ -138,7 +138,7 @@ pub fn sum() -> VMContext {
// wrap into a function
func.define(FunctionContent{
entry: "entry",
entry: "entry",
blocks: {
let mut blocks = HashMap::new();
blocks.insert("entry", blk_entry);
......@@ -147,16 +147,16 @@ pub fn sum() -> VMContext {
blocks
}
});
vm.declare_func(func);
vm
}
#[allow(unused_variables)]
pub fn factorial() -> VMContext {