GitLab will be upgraded on June 2nd 2020 at 2.00 pm (AEDT) to 3.00 pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to local Gitlab admin team.

Commit aeaa960c authored by qinsoon's avatar qinsoon

control flow analysis before inst sel. need more tests

parent f66a2348
use ast::ir::*;
use ast::ptr::*;
use ast::types::*;
use ast::op::*;
use common::vector_as_str;
use std::fmt;
use std::cell::RefCell;
#[derive(Debug, Clone)]
pub struct Instruction {
pub value : Option<Vec<P<TreeNode>>>,
pub ops : RefCell<Vec<P<TreeNode>>>,
pub v: Instruction_
}
impl Instruction {
fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
self.v.debug_str(ops)
}
}
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ops = &self.ops.borrow();
if self.value.is_some() {
write!(f, "{} = {}", vector_as_str(self.value.as_ref().unwrap()), self.v.debug_str(ops))
} else {
write!(f, "{}", self.v.debug_str(ops))
}
}
}
#[derive(Debug, Clone)]
pub enum Instruction_ {
// non-terminal instruction
// expressions
BinOp(BinOp, OpIndex, OpIndex),
CmpOp(CmpOp, OpIndex, OpIndex),
// yields a tuple of results from the call
ExprCall{
data: CallData,
is_abort: bool, // T to abort, F to rethrow
},
// yields the memory value
Load{
is_ptr: bool,
order: MemoryOrder,
mem_loc: OpIndex
},
// yields nothing
Store{
is_ptr: bool,
order: MemoryOrder,
mem_loc: OpIndex,
value: OpIndex
},
// yields pair (oldvalue, boolean (T = success, F = failure))
CmpXchg{
is_ptr: bool,
is_weak: bool,
success_order: MemoryOrder,
fail_order: MemoryOrder,
mem_loc: OpIndex,
expected_value: OpIndex,
desired_value: OpIndex
},
// yields old memory value
AtomicRMW{
is_ptr: bool, // T for iref, F for ptr
order: MemoryOrder,
op: AtomicRMWOp,
mem_loc: OpIndex,
value: OpIndex // operand for op
},
// yields a reference of the type
New(P<MuType>),
// yields an iref of the type
AllocA(P<MuType>),
// yields ref
NewHybrid(P<MuType>, OpIndex),
// yields iref
AllocAHybrid(P<MuType>, OpIndex),
// yields stack ref
NewStack(OpIndex), // func
// TODO: common inst
// yields thread reference
NewThread(OpIndex, Vec<OpIndex>), // stack, args
// yields thread reference (thread resumes with exceptional value)
NewThreadExn(OpIndex, OpIndex), // stack, exception
// yields frame cursor
NewFrameCursor(OpIndex), // stack
// ref<T> -> iref<T>
GetIRef(OpIndex),
// iref|uptr<struct|hybrid<T>> int<M> -> iref|uptr<U>
GetFieldIRef{
is_ptr: bool,
base: OpIndex, // iref or uptr
index: OpIndex // constant
},
// iref|uptr<array<T N>> int<M> -> iref|uptr<T>
GetElementIRef{
is_ptr: bool,
base: OpIndex,
index: OpIndex // can be constant or ssa var
},
// iref|uptr<T> int<M> -> iref|uptr<T>
ShiftIRef{
is_ptr: bool,
base: OpIndex,
offset: OpIndex
},
// iref|uptr<hybrid<T U>> -> iref|uptr<U>
GetVarPartIRef{
is_ptr: bool,
base: OpIndex
},
// PushFrame{
// stack: P<Value>,
// func: P<Value>
// },
// PopFrame{
// stack: P<Value>
// }
Fence(MemoryOrder),
// terminal instruction
Return(Vec<OpIndex>),
ThreadExit, // TODO: common inst
Throw(Vec<OpIndex>),
TailCall(CallData),
Branch1(Destination),
Branch2{
cond: OpIndex,
true_dest: Destination,
false_dest: Destination,
true_prob: f32
},
Watchpoint{ // Watchpoint NONE ResumptionData
// serves as an unconditional trap. Trap to client, and resume with ResumptionData
// Watchpoint (WPID dest) ResumptionData
// when disabled, jump to dest
// when enabled, trap to client and resume
id: Option<WPID>,
disable_dest: Option<Destination>,
resume: ResumptionData
},
WPBranch{
wp: WPID,
disable_dest: Destination,
enable_dest: Destination
},
Call{
data: CallData,
resume: ResumptionData
},
SwapStack{
stack: OpIndex,
is_exception: bool,
args: Vec<OpIndex>,
resume: ResumptionData
},
Switch{
cond: OpIndex,
default: Destination,
branches: Vec<(OpIndex, Destination)>
},
ExnInstruction{
inner: P<Instruction>,
resume: ResumptionData
}
}
impl Instruction_ {
fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
match self {
&Instruction_::BinOp(op, op1, op2) => format!("{:?} {} {}", op, ops[op1], ops[op2]),
&Instruction_::CmpOp(op, op1, op2) => format!("{:?} {} {}", op, ops[op1], ops[op2]),
&Instruction_::ExprCall{ref data, is_abort} => {
let abort = select_value!(is_abort, "ABORT_ON_EXN", "RETHROW");
format!("CALL {} {}", data.debug_str(ops), abort)
},
&Instruction_::Load{is_ptr, mem_loc, order} => {
let ptr = select_value!(is_ptr, "PTR", "");
format!("LOAD {} {:?} {}", ptr, order, ops[mem_loc])
},
&Instruction_::Store{value, is_ptr, mem_loc, order} => {
let ptr = select_value!(is_ptr, "PTR", "");
format!("STORE {} {:?} {} {}", ptr, order, ops[mem_loc], ops[value])
},
&Instruction_::CmpXchg{is_ptr, is_weak, success_order, fail_order,
mem_loc, expected_value, desired_value} => {
let ptr = select_value!(is_ptr, "PTR", "");
let weak = select_value!(is_weak, "WEAK", "");
format!("CMPXCHG {} {} {:?} {:?} {} {} {}",
ptr, weak, success_order, fail_order, ops[mem_loc], ops[expected_value], ops[desired_value])
},
&Instruction_::AtomicRMW{is_ptr, order, op, mem_loc, value} => {
let ptr = select_value!(is_ptr, "PTR", "");
format!("ATOMICRMW {} {:?} {:?} {} {}", ptr, order, op, ops[mem_loc], ops[value])
},
&Instruction_::New(ref ty) => format!("NEW {}", ty),
&Instruction_::AllocA(ref ty) => format!("ALLOCA {}", ty),
&Instruction_::NewHybrid(ref ty, len) => format!("NEWHYBRID {} {}", ty, ops[len]),
&Instruction_::AllocAHybrid(ref ty, len) => format!("ALLOCAHYBRID {} {}", ty, ops[len]),
&Instruction_::NewStack(func) => format!("NEWSTACK {}", ops[func]),
&Instruction_::NewThread(stack, ref args) => format!("NEWTHREAD {} PASS_VALUES {}", ops[stack], op_vector_str(args, ops)),
&Instruction_::NewThreadExn(stack, exn) => format!("NEWTHREAD {} THROW_EXC {}", ops[stack], ops[exn]),
&Instruction_::NewFrameCursor(stack) => format!("NEWFRAMECURSOR {}", ops[stack]),
&Instruction_::GetIRef(reference) => format!("GETIREF {}", ops[reference]),
&Instruction_::GetFieldIRef{is_ptr, base, index} => {
let ptr = select_value!(is_ptr, "PTR", "");
format!("GETFIELDIREF {} {} {}", ptr, ops[base], ops[index])
},
&Instruction_::GetElementIRef{is_ptr, base, index} => {
let ptr = select_value!(is_ptr, "PTR", "");
format!("GETELEMENTIREF {} {} {}", ptr, ops[base], ops[index])
},
&Instruction_::ShiftIRef{is_ptr, base, offset} => {
let ptr = select_value!(is_ptr, "PTR", "");
format!("SHIFTIREF {} {} {}", ptr, ops[base], ops[offset])
},
&Instruction_::GetVarPartIRef{is_ptr, base} => {
let ptr = select_value!(is_ptr, "PTR", "");
format!("GETVARPARTIREF {} {}", ptr, ops[base])
},
&Instruction_::Fence(order) => {
format!("FENCE {:?}", order)
},
&Instruction_::Return(ref vals) => format!("RET {}", op_vector_str(vals, ops)),
&Instruction_::ThreadExit => "THREADEXIT".to_string(),
&Instruction_::Throw(ref vals) => format!("THROW {}", op_vector_str(vals, ops)),
&Instruction_::TailCall(ref call) => format!("TAILCALL {}", call.debug_str(ops)),
&Instruction_::Branch1(ref dest) => format!("BRANCH {}", dest.debug_str(ops)),
&Instruction_::Branch2{cond, ref true_dest, ref false_dest, ..} => {
format!("BRANCH2 {} {} {}", ops[cond], true_dest.debug_str(ops), false_dest.debug_str(ops))
},
&Instruction_::Watchpoint{id, ref disable_dest, ref resume} => {
match id {
Some(id) => {
format!("WATCHPOINT {} {} {}", id, disable_dest.as_ref().unwrap().debug_str(ops), resume.debug_str(ops))
},
None => {
format!("TRAP {}", resume.debug_str(ops))
}
}
},
&Instruction_::WPBranch{wp, ref disable_dest, ref enable_dest} => {
format!("WPBRANCH {} {} {}", wp, disable_dest.debug_str(ops), enable_dest.debug_str(ops))
},
&Instruction_::Call{ref data, ref resume} => format!("CALL {} {}", data.debug_str(ops), resume.debug_str(ops)),
&Instruction_::SwapStack{stack, is_exception, ref args, ref resume} => {
format!("SWAPSTACK {} {} {} {}", ops[stack], is_exception, op_vector_str(args, ops), resume.debug_str(ops))
},
&Instruction_::Switch{cond, ref default, ref branches} => {
let mut ret = format!("SWITCH {} {} {{", ops[cond], default.debug_str(ops));
for i in 0..branches.len() {
let (op, ref dest) = branches[i];
ret.push_str(format!("{} {}", ops[op], dest.debug_str(ops)).as_str());
if i != branches.len() - 1 {
ret.push_str(", ");
}
}
ret.push_str("}}");
ret
},
&Instruction_::ExnInstruction{ref inner, ref resume} => {
format!("{} {}", inner.debug_str(ops), resume.debug_str(ops))
}
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum MemoryOrder {
NotAtomic,
Relaxed,
Consume,
Acquire,
Release,
AcqRel,
SeqCst
}
#[derive(Copy, Clone, Debug)]
pub enum CallConvention {
Mu,
Foreign(ForeignFFI)
}
#[derive(Copy, Clone, Debug)]
pub enum ForeignFFI {
C
}
#[derive(Clone, Debug)]
pub struct CallData {
pub func: OpIndex,
pub args: Vec<OpIndex>,
pub convention: CallConvention
}
impl CallData {
fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
format!("{:?} {} [{}]", self.convention, ops[self.func], op_vector_str(&self.args, ops))
}
}
#[derive(Clone, Debug)]
pub struct ResumptionData {
pub normal_dest: Destination,
pub exn_dest: Destination
}
impl ResumptionData {
fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
format!("normal: {}, exception: {}", self.normal_dest.debug_str(ops), self.exn_dest.debug_str(ops))
}
}
#[derive(Clone, Debug)]
pub struct Destination {
pub target: MuTag,
pub args: Vec<DestArg>
}
impl Destination {
fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
let mut ret = format!("{}", self.target);
ret.push('[');
for i in 0..self.args.len() {
let ref arg = self.args[i];
ret.push_str(arg.debug_str(ops).as_str());
if i != self.args.len() - 1 {
ret.push_str(", ");
}
}
ret.push(']');
ret
}
}
#[derive(Clone, Debug)]
pub enum DestArg {
Normal(OpIndex),
Freshbound(usize)
}
impl DestArg {
fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
match self {
&DestArg::Normal(index) => format!("{}", ops[index]),
&DestArg::Freshbound(n) => format!("${}", n)
}
}
}
\ No newline at end of file
use ast::ptr::P;
use ast::op::*;
use ast::types::*;
use ast::inst::*;
use common::vector_as_str;
use std::collections::HashMap;
use std::fmt;
use std::default;
use std::cell::Cell;
use std::cell::RefCell;
pub type WPID = usize;
pub type MuID = usize;
......@@ -22,33 +23,6 @@ pub struct MuFunction {
pub context: FunctionContext
}
#[derive(Debug)]
pub struct FunctionContent {
pub entry: MuTag,
pub blocks: Vec<(MuTag, Block)>
}
#[derive(Debug)]
pub struct FunctionContext {
pub values: HashMap<MuID, ValueEntry>
}
impl FunctionContext {
fn new() -> FunctionContext {
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)
}
}
impl MuFunction {
pub fn new(fn_name: MuTag, sig: P<MuFuncSig>) -> MuFunction {
MuFunction{fn_name: fn_name, sig: sig, content: None, context: FunctionContext::new()}
......@@ -87,18 +61,102 @@ impl MuFunction {
}
}
#[derive(Debug)]
pub struct FunctionContent {
pub entry: MuTag,
pub blocks: HashMap<MuTag, Block>
}
impl FunctionContent {
pub fn get_entry_block(&self) -> &Block {
self.get_block(self.entry).unwrap()
}
pub fn get_entry_block_mut(&mut self) -> &mut Block {
self.get_block_mut(self.entry).unwrap()
}
pub fn get_block(&self, tag: MuTag) -> Option<&Block> {
self.blocks.get(tag)
}
pub fn get_block_mut(&mut self, tag: MuTag) -> Option<&mut Block> {
self.blocks.get_mut(tag)
}
}
#[derive(Debug)]
pub struct FunctionContext {
pub values: HashMap<MuID, ValueEntry>
}
impl FunctionContext {
fn new() -> FunctionContext {
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)
}
}
#[derive(Debug)]
pub struct Block {
pub label: MuTag,
pub content: Option<BlockContent>
pub content: Option<BlockContent>,
pub control_flow: ControlFlow
}
impl Block {
pub fn new(label: MuTag) -> Block {
Block{label: label, content: None}
Block{label: label, content: None, control_flow: ControlFlow::default()}
}
}
#[derive(Debug)]
pub struct ControlFlow {
pub preds : Vec<MuTag>,
pub succs : Vec<BlockEdge>
}
impl fmt::Display for ControlFlow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "preds: [{}], ", vector_as_str(&self.preds)).unwrap();
write!(f, "succs: [{}]", vector_as_str(&self.succs))
}
}
impl default::Default for ControlFlow {
fn default() -> ControlFlow {
ControlFlow {preds: vec![], succs: vec![]}
}
}
#[derive(Copy, Clone, Debug)]
pub struct BlockEdge {
pub target: MuTag,
pub kind: EdgeKind,
pub is_exception: bool,
pub probability: f32
}
impl fmt::Display for BlockEdge {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} ({:?}{} - {})", self.target, self.kind, select_value!(self.is_exception, ", exceptional", ""), self.probability)
}
}
#[derive(Copy, Clone, Debug)]
pub enum EdgeKind {
Forward, Backward
}
#[derive(Debug)]
pub struct BlockContent {
pub args: Vec<P<TreeNode>>,
......@@ -177,7 +235,12 @@ pub struct ValueEntry {
pub id: MuID,
pub tag: MuTag,
pub ty: P<MuType>,
pub use_count: Cell<usize>, // how many times this entry is used
// 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>
}
......@@ -227,390 +290,7 @@ impl fmt::Display for Constant {
}
}
#[derive(Debug, Clone)]
pub struct Instruction {
pub value : Option<Vec<P<TreeNode>>>,
pub ops : RefCell<Vec<P<TreeNode>>>,
pub v: Instruction_
}
impl Instruction {
fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
self.v.debug_str(ops)
}
}
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ops = &self.ops.borrow();
if self.value.is_some() {
write!(f, "{} = {}", node_vector_str(self.value.as_ref().unwrap()), self.v.debug_str(ops))
} else {
write!(f, "{}", self.v.debug_str(ops))
}
}
}
#[derive(Debug, Clone)]
pub enum Instruction_ {
// non-terminal instruction
// expressions
BinOp(BinOp, OpIndex, OpIndex),
CmpOp(CmpOp, OpIndex, OpIndex),
// yields a tuple of results from the call
ExprCall{
data: CallData,
is_abort: bool, // T to abort, F to rethrow
},
// yields the memory value
Load{
is_ptr: bool,
order: MemoryOrder,
mem_loc: OpIndex
},
// yields nothing
Store{
is_ptr: bool,
order: MemoryOrder,
mem_loc: OpIndex,
value: OpIndex
},
// yields pair (oldvalue, boolean (T = success, F = failure))
CmpXchg{
is_ptr: bool,
is_weak: bool,
success_order: MemoryOrder,
fail_order: MemoryOrder,
mem_loc: OpIndex,
expected_value: OpIndex,
desired_value: OpIndex
},
// yields old memory value
AtomicRMW{
is_ptr: bool, // T for iref, F for ptr
order: MemoryOrder,
op: AtomicRMWOp,
mem_loc: OpIndex,
value: OpIndex // operand for op
},
// yields a reference of the type
New(P<MuType>),
// yields an iref of the type
AllocA(P<MuType>),
// yields ref
NewHybrid(P<MuType>, OpIndex),
// yields iref
AllocAHybrid(P<MuType>, OpIndex),
// yields stack ref
NewStack(OpIndex), // func
// TODO: common inst
// yields thread reference
NewThread(OpIndex, Vec<OpIndex>), // stack, args
// yields thread reference (thread resumes with exceptional value)
NewThreadExn(OpIndex, OpIndex), // stack, exception
// yields frame cursor
NewFrameCursor(OpIndex), // stack
// ref<T> -> iref<T>
GetIRef(OpIndex),
// iref|uptr<struct|hybrid<T>> int<M> -> iref|uptr<U>
GetFieldIRef{
is_ptr: bool,
base: OpIndex, // iref or uptr
index: OpIndex // constant
},
// iref|uptr<array<T N>> int<M> -> iref|uptr<T>
GetElementIRef{
is_ptr: bool,
base: OpIndex,
index: OpIndex // can be constant or ssa var
},
// iref|uptr<T> int<M> -> iref|uptr<T>
ShiftIRef{
is_ptr: bool,
base: OpIndex,
offset: OpIndex
},
// iref|uptr<hybrid<T U>> -> iref|uptr<U>
GetVarPartIRef{
is_ptr: bool,
base: OpIndex
},
// PushFrame{
// stack: P<Value>,
// func: P<Value>
// },
// PopFrame{
// stack: P<Value>
// }
Fence(MemoryOrder),
// terminal instruction
Return(Vec<OpIndex>),
ThreadExit, // TODO: common inst
Throw(Vec<OpIndex>),
TailCall(CallData),
Branch1(Destination),
Branch2{
cond: OpIndex,
true_dest: Destination,
false_dest: Destination
},
Watchpoint{ // Watchpoint NONE ResumptionData
// serves as an unconditional trap. Trap to client, and resume with ResumptionData
// Watchpoint (WPID dest) ResumptionData
// when disabled, jump to dest
// when enabled, trap to client and resume
id: Option<WPID>,
disable_dest: Option<Destination>,
resume: ResumptionData
},
WPBranch{
wp: WPID,
disable_dest: Destination,
enable_dest: Destination
},
Call{
data: CallData,
resume: ResumptionData
},
SwapStack{
stack: OpIndex,
is_exception: bool,
args: Vec<OpIndex>,
resume: ResumptionData
},
Switch{
cond: OpIndex,
default: Destination,
branches: Vec<(OpIndex, Destination)>
},
ExnInstruction{
inner: P<Instruction>,
resume: ResumptionData
}
}
macro_rules! select {