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

Commit 0a59c2d1 authored by qinsoon's avatar qinsoon
Browse files

refactoring and adding comments in ast crate

parent 3bc3c345
......@@ -2,22 +2,27 @@ use ir::*;
use ptr::*;
use types::*;
use op::*;
use ir_semantics;
use utils::vec_utils;
use std::fmt;
use std::sync::RwLock;
#[derive(Debug)]
// this implements RustcEncodable, RustcDecodable, Clone and Display
/// Instruction represents a Mu instruction
#[derive(Debug)] // RustcEncodable, RustcDecodable, Clone and Display
pub struct Instruction {
pub hdr: MuEntityHeader,
/// the values this instruction holds
pub value : Option<Vec<P<Value>>>,
/// ops field list all the children nodes,
/// and in Instruction_, the children nodes are referred by indices
/// This design makes it easy for the compiler to iterate through all the children
pub ops : RwLock<Vec<P<TreeNode>>>,
/// used for pattern matching
pub v: Instruction_
}
// Instruction implements MuEntity
impl_mu_entity!(Instruction);
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
......@@ -69,12 +74,172 @@ impl Clone for Instruction {
}
impl Instruction {
pub fn is_terminal_inst(&self) -> bool {
use inst::Instruction_::*;
match self.v {
BinOp(_, _, _)
| BinOpWithStatus(_, _, _, _)
| CmpOp(_, _, _)
| ConvOp{..}
| ExprCall{..}
| ExprCCall{..}
| Load{..}
| Store{..}
| CmpXchg{..}
| AtomicRMW{..}
| New(_)
| AllocA(_)
| NewHybrid(_, _)
| AllocAHybrid(_, _)
| NewStack(_)
| NewThread(_, _)
| NewThreadExn(_, _)
| NewFrameCursor(_)
| GetIRef(_)
| GetFieldIRef{..}
| GetElementIRef{..}
| ShiftIRef{..}
| GetVarPartIRef{..}
| Select{..}
| Fence(_)
| CommonInst_GetThreadLocal
| CommonInst_SetThreadLocal(_)
| CommonInst_Pin(_)
| CommonInst_Unpin(_)
| Move(_)
| PrintHex(_) => false,
Return(_)
| ThreadExit
| Throw(_)
| TailCall(_)
| Branch1(_)
| Branch2{..}
| Watchpoint{..}
| WPBranch{..}
| Call{..}
| CCall{..}
| SwapStack{..}
| Switch{..}
| ExnInstruction{..} => true
}
}
pub fn is_non_terminal_inst(&self) -> bool {
!self.is_terminal_inst()
}
// FIXME: need to check correctness
pub fn has_side_effect(&self) -> bool {
use inst::Instruction_::*;
match self.v {
BinOp(_, _, _) => false,
BinOpWithStatus(_, _, _, _) => false,
CmpOp(_, _, _) => false,
ConvOp{..} => false,
ExprCall{..} => true,
ExprCCall{..} => true,
Load{..} => true,
Store{..} => true,
CmpXchg{..} => true,
AtomicRMW{..} => true,
New(_) => true,
AllocA(_) => true,
NewHybrid(_, _) => true,
AllocAHybrid(_, _) => true,
NewStack(_) => true,
NewThread(_, _) => true,
NewThreadExn(_, _) => true,
NewFrameCursor(_) => true,
GetIRef(_) => false,
GetFieldIRef{..} => false,
GetElementIRef{..} => false,
ShiftIRef{..} => false,
GetVarPartIRef{..} => false,
Fence(_) => true,
Return(_) => true,
ThreadExit => true,
Throw(_) => true,
TailCall(_) => true,
Branch1(_) => true,
Branch2{..} => true,
Select{..} => false,
Watchpoint{..} => true,
WPBranch{..} => true,
Call{..} => true,
CCall{..} => true,
SwapStack{..} => true,
Switch{..} => true,
ExnInstruction{..} => true,
CommonInst_GetThreadLocal => true,
CommonInst_SetThreadLocal(_) => true,
CommonInst_Pin(_) => true,
CommonInst_Unpin(_) => true,
Move(_) => false,
PrintHex(_) => true
}
}
pub fn is_potentially_excepting_instruction(&self) -> bool {
use inst::Instruction_::*;
match self.v {
Watchpoint{..}
| Call{..}
| CCall{..}
| SwapStack{..}
| ExnInstruction{..} => true,
BinOp(_, _, _)
| BinOpWithStatus(_, _, _, _)
| CmpOp(_, _, _)
| ConvOp{..}
| ExprCall{..}
| ExprCCall{..}
| Load{..}
| Store{..}
| CmpXchg{..}
| AtomicRMW{..}
| New(_)
| AllocA(_)
| NewHybrid(_, _)
| AllocAHybrid(_, _)
| NewStack(_)
| NewThread(_, _)
| NewThreadExn(_, _)
| NewFrameCursor(_)
| GetIRef(_)
| GetFieldIRef{..}
| GetElementIRef{..}
| ShiftIRef{..}
| GetVarPartIRef{..}
| Fence(_)
| Return(_)
| ThreadExit
| Throw(_)
| TailCall(_)
| Branch1(_)
| Branch2{..}
| Select{..}
| WPBranch{..}
| Switch{..}
| CommonInst_GetThreadLocal
| CommonInst_SetThreadLocal(_)
| CommonInst_Pin(_)
| CommonInst_Unpin(_)
| Move(_)
| PrintHex(_) => false
}
}
pub fn has_exception_clause(&self) -> bool {
ir_semantics::is_potentially_excepting_instruction(&self.v)
self.is_potentially_excepting_instruction()
}
pub fn get_exception_target(&self) -> Option<MuID> {
use inst::Instruction_::*;
match self.v {
Watchpoint {ref resume, ..}
| Call {ref resume, ..}
......@@ -625,3 +790,15 @@ impl DestArg {
}
}
}
fn op_vector_str(vec: &Vec<OpIndex>, ops: &Vec<P<TreeNode>>) -> String {
let mut ret = String::new();
for i in 0..vec.len() {
let index = vec[i];
ret.push_str(format!("{}", ops[index]).as_str());
if i != vec.len() - 1 {
ret.push_str(", ");
}
}
ret
}
\ No newline at end of file
......@@ -33,19 +33,21 @@ lazy_static! {
a.store(INTERNAL_ID_START, Ordering::SeqCst);
a
};
}
}
/// MuID reserved for machine registers
pub const MACHINE_ID_START : usize = 0;
pub const MACHINE_ID_END : usize = 200;
/// MuID reserved for internal types, etc.
pub const INTERNAL_ID_START: usize = 201;
pub const INTERNAL_ID_END : usize = 500;
pub const USER_ID_START : usize = 1001;
#[deprecated]
#[allow(dead_code)]
/// it could happen that one same machine register get different IDs
/// during serialization and restoring
/// currently I hand-write fixed ID for each machine register
// it could happen that one same machine register get different IDs
// during serialization and restoring
// currently I hand-write fixed ID for each machine register
pub fn new_machine_id() -> MuID {
let ret = MACHINE_ID.fetch_add(1, Ordering::SeqCst);
if ret >= MACHINE_ID_END {
......@@ -62,6 +64,9 @@ pub fn new_internal_id() -> MuID {
ret
}
/// MuFunction represents a Mu function (not a specific definition of a function)
/// This stores function signature, and a list of all versions of this function (as ID),
/// and its current version (as ID)
#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct MuFunction {
pub hdr: MuEntityHeader,
......@@ -80,7 +85,8 @@ impl MuFunction {
all_vers: vec![]
}
}
/// adds a new version to this function, and it becomes the current version
pub fn new_version(&mut self, fv: MuID) {
if self.cur_ver.is_some() {
let obsolete_ver = self.cur_ver.unwrap();
......@@ -97,22 +103,23 @@ impl fmt::Display for MuFunction {
}
}
/// MuFunctionVersion represents a specific definition of a Mu function
/// It owns the tree structure of MuIRs for the function version
// FIXME: currently part of compilation information is also stored in this data structure
// we should move them (see Issue #18)
#[derive(RustcEncodable, RustcDecodable)]
pub struct MuFunctionVersion {
pub hdr: MuEntityHeader,
pub func_id: MuID,
pub sig: P<MuFuncSig>,
orig_content: Option<FunctionContent>,
pub content: Option<FunctionContent>,
orig_content: Option<FunctionContent>, // original IR
pub content: Option<FunctionContent>, // IR that may have been rewritten during compilation
is_defined: bool,
is_compiled: bool,
pub context: FunctionContext,
pub force_inline: bool,
pub block_trace: Option<Vec<MuID>> // only available after Trace Generation Pass
}
......@@ -141,6 +148,7 @@ impl fmt::Debug for MuFunctionVersion {
}
impl MuFunctionVersion {
/// creates an empty function version
pub fn new(entity: MuEntityHeader, func: MuID, sig: P<MuFuncSig>) -> MuFunctionVersion {
MuFunctionVersion{
hdr: entity,
......@@ -156,6 +164,7 @@ impl MuFunctionVersion {
}
}
/// creates a complete function version
pub fn new_(hdr: MuEntityHeader, id: MuID, sig: P<MuFuncSig>, content: FunctionContent, context: FunctionContext) -> MuFunctionVersion {
MuFunctionVersion {
hdr: hdr,
......@@ -175,6 +184,7 @@ impl MuFunctionVersion {
self.orig_content.as_ref()
}
/// defines function content
pub fn define(&mut self, content: FunctionContent) {
if self.is_defined {
panic!("alread defined the function: {}", self);
......@@ -188,6 +198,7 @@ impl MuFunctionVersion {
pub fn is_compiled(&self) -> bool {
self.is_compiled
}
pub fn set_compiled(&mut self) {
self.is_compiled = true;
}
......@@ -225,7 +236,8 @@ impl MuFunctionVersion {
})
}
/// get Map(CallSiteID -> FuncID) that are called by this function
/// gets call outedges in this function
/// returns Map(CallSiteID -> FuncID)
pub fn get_static_call_edges(&self) -> LinkedHashMap<MuID, MuID> {
let mut ret = LinkedHashMap::new();
......@@ -296,13 +308,12 @@ impl MuFunctionVersion {
}
}
/// FunctionContent contains all blocks (which include all instructions) for the function
#[derive(Clone, RustcEncodable, RustcDecodable)]
pub struct FunctionContent {
pub entry: MuID,
pub blocks: LinkedHashMap<MuID, Block>,
// this field only valid after control flow analysis
pub exception_blocks: LinkedHashSet<MuID>
pub exception_blocks: LinkedHashSet<MuID> // this field only valid after control flow analysis
}
impl fmt::Debug for FunctionContent {
......@@ -355,6 +366,9 @@ impl FunctionContent {
}
}
/// FunctionContext contains compilation information about the function
// FIXME: should move this out of ast crate and bind its lifetime with compilation (Issue #18)
#[derive(Default, Debug, RustcEncodable, RustcDecodable)]
pub struct FunctionContext {
pub values: LinkedHashMap<MuID, SSAVarEntry>
......@@ -366,7 +380,8 @@ impl FunctionContext {
values: LinkedHashMap::new()
}
}
/// makes a TreeNode of an SSA variable
pub fn make_temporary(&mut self, id: MuID, ty: P<MuType>) -> P<TreeNode> {
let val = P(Value{
hdr: MuEntityHeader::unnamed(id),
......@@ -381,6 +396,7 @@ impl FunctionContext {
})
}
/// shows the name for an SSA by ID
pub fn get_temp_display(&self, id: MuID) -> String {
match self.get_value(id) {
Some(entry) => format!("{}", entry.value()),
......@@ -388,15 +404,20 @@ impl FunctionContext {
}
}
/// returns a &SSAVarEntry for the given ID
pub fn get_value(&self, id: MuID) -> Option<&SSAVarEntry> {
self.values.get(&id)
}
/// returns a &mut SSAVarEntry for the given ID
pub fn get_value_mut(&mut self, id: MuID) -> Option<&mut SSAVarEntry> {
self.values.get_mut(&id)
}
}
/// Block contains BlockContent, which includes all the instructions for the block
// FIXME: control_flow field should be moved out of ast crate (Issue #18)
#[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct Block {
pub hdr: MuEntityHeader,
......@@ -422,11 +443,13 @@ impl Block {
pub fn new(entity: MuEntityHeader) -> Block {
Block{hdr: entity, content: None, control_flow: ControlFlow::default()}
}
/// does this block have an exception arguments?
pub fn is_receiving_exception_arg(&self) -> bool {
return self.content.as_ref().unwrap().exn_arg.is_some()
}
/// how many IR instruction does this block have?
pub fn number_of_irs(&self) -> usize {
if self.content.is_none() {
0
......@@ -438,6 +461,9 @@ impl Block {
}
}
/// ControlFlow stores compilation info about control flows of a block
// FIXME: Issue #18
#[derive(Debug, RustcEncodable, RustcDecodable, Clone)]
pub struct ControlFlow {
pub preds : Vec<MuID>,
......@@ -445,6 +471,8 @@ pub struct ControlFlow {
}
impl ControlFlow {
/// returns the successor with highest branching probability
/// (in case of tie, returns first met successor)
pub fn get_hottest_succ(&self) -> Option<MuID> {
if self.succs.len() == 0 {
None
......@@ -477,6 +505,7 @@ impl default::Default for ControlFlow {
}
}
/// BlockEdge represents an edge in control flow graph
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct BlockEdge {
pub target: MuID,
......@@ -496,6 +525,7 @@ pub enum EdgeKind {
Forward, Backward
}
/// BlockContent describes arguments to this block, and owns all the IR instructions
#[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct BlockContent {
pub args: Vec<P<Value>>,
......@@ -521,6 +551,7 @@ impl fmt::Debug for BlockContent {
}
impl BlockContent {
/// returns all the arguments passed to its successors
pub fn get_out_arguments(&self) -> Vec<P<Value>> {
let n_insts = self.body.len();
let ref last_inst = self.body[n_insts - 1];
......@@ -592,26 +623,30 @@ impl BlockContent {
}
}
/// TreeNode represents a node in the AST, it could either be an instruction,
/// or an value (SSA, constant, global, etc)
#[derive(Debug, RustcEncodable, RustcDecodable, Clone)]
/// always use with P<TreeNode>
pub struct TreeNode {
pub v: TreeNode_,
}
impl TreeNode {
// this is a hack to allow creating TreeNode without using a &mut MuFunctionVersion
/// creates a sharable Instruction TreeNode
pub fn new_inst(v: Instruction) -> P<TreeNode> {
P(TreeNode{
v: TreeNode_::Instruction(v),
})
}
/// creates an owned Instruction TreeNode
pub fn new_boxed_inst(v: Instruction) -> Box<TreeNode> {
Box::new(TreeNode{
v: TreeNode_::Instruction(v),
})
}
/// extracts the MuID of an SSA TreeNode
/// if the node is not an SSA, returns None
pub fn extract_ssa_id(&self) -> Option<MuID> {
match self.v {
TreeNode_::Value(ref pv) => {
......@@ -624,6 +659,9 @@ impl TreeNode {
}
}
/// clones the value from the TreeNode
/// * if this is a Instruction TreeNode, returns its first result value
/// * if this is a value, returns a clone of it
pub fn clone_value(&self) -> P<Value> {
match self.v {
TreeNode_::Value(ref val) => val.clone(),
......@@ -637,6 +675,7 @@ impl TreeNode {
}
}
/// consumes the TreeNode, returns the value in it (or None if it is not a value)
pub fn into_value(self) -> Option<P<Value>> {
match self.v {
TreeNode_::Value(val) => Some(val),
......@@ -644,6 +683,7 @@ impl TreeNode {
}
}
/// consumes the TreeNode, returns the instruction in it (or None if it is not an instruction)
pub fn into_inst(self) -> Option<Instruction> {
match self.v {
TreeNode_::Instruction(inst) => Some(inst),
......@@ -652,7 +692,6 @@ impl TreeNode {
}
}
/// use +() to display a node
impl fmt::Display for TreeNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.v {
......@@ -664,13 +703,18 @@ impl fmt::Display for TreeNode {
}
}
/// TreeNode_ is used for pattern matching for TreeNode
#[derive(Debug, RustcEncodable, RustcDecodable, Clone)]
pub enum TreeNode_ {
Value(P<Value>),
Instruction(Instruction)
}
/// always use with P<Value>
/// Value represents a value in the tree, it could be SSA variables, constants, globals,
/// which all will appear in Mu IR. Value may also represent a memory (as in transformed tree,
/// we need to represent memory as well)
///
/// Value should always be used with P<Value> (sharable)
#[derive(PartialEq, RustcEncodable, RustcDecodable)]
pub struct Value {
pub hdr: MuEntityHeader,
......@@ -679,6 +723,7 @@ pub struct Value {
}
impl Value {
/// creates an int constant value
pub fn make_int_const(id: MuID, val: u64) -> P<Value> {
P(Value{
hdr: MuEntityHeader::unnamed(id),
......@@ -707,6 +752,9 @@ impl Value {
}
}
/// disguises a value as another type.
/// This is usually used for treat an integer type as an integer of a different length
/// This method is unsafe
pub unsafe fn as_type(&self, ty: P<MuType>) -> P<Value> {
P(Value{
hdr: self.hdr.clone(),
......@@ -738,11 +786,11 @@ impl Value {
}
}
pub fn extract_int_const(&self) -> u64 {
pub fn extract_int_const(&self) -> Option<u64> {
match self.v {
Value_::Constant(Constant::Int(val)) => val,
Value_::Constant(Constant::NullRef) => 0,
_ => panic!("expect int const")
Value_::Constant(Constant::Int(val)) => Some(val),
Value_::Constant(Constant::NullRef) => Some(0),
_ => None
}
}
......@@ -800,6 +848,7 @@ impl fmt::Display for Value {
}
}
/// Value_ is used for pattern matching for Value
#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub enum Value_ {
SSAVar(MuID),
......@@ -808,12 +857,14 @@ pub enum Value_ {
Memory(MemoryLocation)
}
/// SSAVarEntry represent compilation info for an SSA variable
// FIXME: Issue#18
#[derive(Debug)]
pub struct SSAVarEntry {
val: P<Value>,
// how many times this entry is used
// availalbe after DefUse pass
// available after DefUse pass
use_count: AtomicUsize,
// this field is only used during TreeGeneration pass
......@@ -901,19 +952,25 @@ impl fmt::Display for SSAVarEntry {
}
}
/// Constant presents all kinds of constant that can appear in MuIR
#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub enum Constant {
/// all integer constants are stored as u64
Int(u64),
/// float constants
Float(f32),
/// double constants
Double(f64),
// IRef(Address),
/// function reference
FuncRef(MuID),
/// vector constant (currently not used)
Vector(Vec<Constant>),
//Pointer(Address),
/// null reference
NullRef,
/// external symbol
ExternSym(CName),
List(Vec<P<Value>>) // a composite type of several constants
/// a composite type of several constants (currently not used)
List(Vec<P<Value>>)
}
impl fmt::Display for Constant {
......@@ -948,15 +1005,19 @@ impl fmt::Display for Constant {
}
}
/// MemoryLocation represents a memory value
/// This enumerate type is target dependent
#[cfg(target_arch = "x86_64")]
#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub enum MemoryLocation {
/// addr = base + offset + index * scale
Address{
base: P<Value>,
offset: Option<P<Value>>,
index: Option<P<Value>>,
scale: Option<u8>
},