GitLab will continue to be upgraded from 11.4.5-ce.0 on November 25th 2019 at 4.00pm (AEDT) to 5.00pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available.

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 {
let mut vm = VMContext::new();
// .typedef @int_64 = int<64>