GitLab will be upgraded to the 12.10.14-ce.0 on 28 Sept 2020 at 2.00pm (AEDT) to 2.30pm (AEDT). During the update, GitLab and Mattermost services will not be available. If you have any concerns with this, please talk to us at N110 (b) CSIT building.

inst.rs 14.3 KB
Newer Older
qinsoon's avatar
qinsoon committed
1 2 3 4 5
use ir::*;
use ptr::*;
use types::*;
use op::*;

6
use utils::vec_utils;
7 8

use std::fmt;
qinsoon's avatar
qinsoon committed
9
use std::sync::RwLock;
10

qinsoon's avatar
qinsoon committed
11
#[derive(Debug)]
12
pub struct Instruction {
qinsoon's avatar
qinsoon committed
13
    pub hdr: MuEntityHeader,
qinsoon's avatar
qinsoon committed
14
    pub value : Option<Vec<P<Value>>>,
qinsoon's avatar
qinsoon committed
15
    pub ops : RwLock<Vec<P<TreeNode>>>,
16 17 18
    pub v: Instruction_
}

qinsoon's avatar
qinsoon committed
19 20
impl_mu_entity!(Instruction);

qinsoon's avatar
qinsoon committed
21 22 23
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
impl Encodable for Instruction {
    fn encode<S: Encoder> (&self, s: &mut S) -> Result<(), S::Error> {
qinsoon's avatar
qinsoon committed
24 25 26
        s.emit_struct("Instruction", 4, |s| {
            try!(s.emit_struct_field("hdr", 0, |s| self.hdr.encode(s)));
            try!(s.emit_struct_field("value", 1, |s| self.value.encode(s)));
qinsoon's avatar
qinsoon committed
27 28
            
            let ops = &self.ops.read().unwrap();
qinsoon's avatar
qinsoon committed
29
            try!(s.emit_struct_field("ops", 2, |s| ops.encode(s)));
qinsoon's avatar
qinsoon committed
30
            
qinsoon's avatar
qinsoon committed
31
            try!(s.emit_struct_field("v", 3, |s| self.v.encode(s)));
qinsoon's avatar
qinsoon committed
32 33 34 35 36 37 38 39
            
            Ok(()) 
        })        
    }
}

impl Decodable for Instruction {
    fn decode<D: Decoder>(d: &mut D) -> Result<Instruction, D::Error> {
qinsoon's avatar
qinsoon committed
40 41 42
        d.read_struct("Instruction", 4, |d| {
            let hdr = try!(d.read_struct_field("hdr", 0, |d| Decodable::decode(d)));
            let value = try!(d.read_struct_field("value", 1, |d| Decodable::decode(d)));
qinsoon's avatar
qinsoon committed
43
            
qinsoon's avatar
qinsoon committed
44
            let ops = try!(d.read_struct_field("ops", 2, |d| Decodable::decode(d)));
qinsoon's avatar
qinsoon committed
45
            
qinsoon's avatar
qinsoon committed
46
            let v = try!(d.read_struct_field("v", 3, |d| Decodable::decode(d)));
qinsoon's avatar
qinsoon committed
47 48
            
            Ok(Instruction{
qinsoon's avatar
qinsoon committed
49
                hdr: hdr,
qinsoon's avatar
qinsoon committed
50 51 52 53 54 55 56 57 58 59 60
                value: value,
                ops: RwLock::new(ops),
                v: v
            })
        })
    }
}

impl Clone for Instruction {
    fn clone(&self) -> Self {
        Instruction {
qinsoon's avatar
qinsoon committed
61
            hdr: self.hdr.clone(),
qinsoon's avatar
qinsoon committed
62 63 64 65 66 67 68
            value: self.value.clone(),
            ops: RwLock::new(self.ops.read().unwrap().clone()),
            v: self.v.clone()
        }
    }
}

69 70 71 72 73 74 75 76
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 {
qinsoon's avatar
qinsoon committed
77
        let ops = &self.ops.read().unwrap();
78
        if self.value.is_some() {
qinsoon's avatar
qinsoon committed
79
            write!(f, "{} = {}", vec_utils::as_str(self.value.as_ref().unwrap()), self.v.debug_str(ops))
80 81 82 83 84 85
        } else {
            write!(f, "{}", self.v.debug_str(ops))
        }
    }
}

qinsoon's avatar
qinsoon committed
86
#[derive(Debug, Clone, RustcEncodable, RustcDecodable)]
87 88
pub enum Instruction_ {
    // non-terminal instruction
89

90
    // expressions
91 92

    BinOp(BinOp, OpIndex, OpIndex),
93
    CmpOp(CmpOp, OpIndex, OpIndex),
94

95 96 97
    // yields a tuple of results from the call
    ExprCall{
        data: CallData,
qinsoon's avatar
qinsoon committed
98
        is_abort: bool, // T to abort, F to rethrow - FIXME: current, always rethrow for now
99
    },
100

101 102 103 104 105 106
    // yields the memory value
    Load{
        is_ptr: bool,
        order: MemoryOrder,
        mem_loc: OpIndex
    },
107

108 109 110
    // yields nothing
    Store{
        is_ptr: bool,
111
        order: MemoryOrder,
112 113 114
        mem_loc: OpIndex,
        value: OpIndex
    },
115

116 117 118 119 120 121 122 123 124 125
    // 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
    },
126

127 128 129 130 131 132 133 134
    // 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
    },
135

136 137
    // yields a reference of the type
    New(P<MuType>),
138

139 140
    // yields an iref of the type
    AllocA(P<MuType>),
141

142 143
    // yields ref
    NewHybrid(P<MuType>, OpIndex),
144

145 146
    // yields iref
    AllocAHybrid(P<MuType>, OpIndex),
147

148 149 150
    // yields stack ref
    NewStack(OpIndex), // func
                           // TODO: common inst
151

152 153
    // yields thread reference
    NewThread(OpIndex, Vec<OpIndex>), // stack, args
154

155 156
    // yields thread reference (thread resumes with exceptional value)
    NewThreadExn(OpIndex, OpIndex), // stack, exception
157

158 159
    // yields frame cursor
    NewFrameCursor(OpIndex), // stack
160

161 162
    // ref<T> -> iref<T>
    GetIRef(OpIndex),
163

164 165 166 167 168 169
    // iref|uptr<struct|hybrid<T>> int<M> -> iref|uptr<U>
    GetFieldIRef{
        is_ptr: bool,
        base: OpIndex, // iref or uptr
        index: OpIndex // constant
    },
170

171 172 173 174 175 176
    // iref|uptr<array<T N>> int<M> -> iref|uptr<T>
    GetElementIRef{
        is_ptr: bool,
        base: OpIndex,
        index: OpIndex // can be constant or ssa var
    },
177

178 179 180 181 182 183
    // iref|uptr<T> int<M> -> iref|uptr<T>
    ShiftIRef{
        is_ptr: bool,
        base: OpIndex,
        offset: OpIndex
    },
184

185 186 187 188 189
    // iref|uptr<hybrid<T U>> -> iref|uptr<U>
    GetVarPartIRef{
        is_ptr: bool,
        base: OpIndex
    },
190

191 192 193 194 195 196 197 198 199
//    PushFrame{
//        stack: P<Value>,
//        func: P<Value>
//    },
//    PopFrame{
//        stack: P<Value>
//    }

    Fence(MemoryOrder),
200

201 202 203
    // terminal instruction
    Return(Vec<OpIndex>),
    ThreadExit, // TODO:  common inst
204
    Throw(OpIndex),
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
    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
221
    },
222
    WPBranch{
223
        wp: WPID,
224 225 226 227 228 229 230
        disable_dest: Destination,
        enable_dest: Destination
    },
    Call{
        data: CallData,
        resume: ResumptionData
    },
qinsoon's avatar
qinsoon committed
231 232 233 234
    CCall{
        data: CallData,
        resume: ResumptionData
    },
235 236 237 238 239 240 241 242 243 244 245 246
    SwapStack{
        stack: OpIndex,
        is_exception: bool,
        args: Vec<OpIndex>,
        resume: ResumptionData
    },
    Switch{
        cond: OpIndex,
        default: Destination,
        branches: Vec<(OpIndex, Destination)>
    },
    ExnInstruction{
qinsoon's avatar
qinsoon committed
247
        inner: Box<Instruction>,
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
        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", "");
263
                format!("LOAD {} {:?} {}", ptr, order, ops[mem_loc])
264 265 266 267 268
            },
            &Instruction_::Store{value, is_ptr, mem_loc, order} => {
                let ptr = select_value!(is_ptr, "PTR", "");
                format!("STORE {} {:?} {} {}", ptr, order, ops[mem_loc], ops[value])
            },
269
            &Instruction_::CmpXchg{is_ptr, is_weak, success_order, fail_order,
270 271 272
                mem_loc, expected_value, desired_value} => {
                let ptr = select_value!(is_ptr, "PTR", "");
                let weak = select_value!(is_weak, "WEAK", "");
273 274
                format!("CMPXCHG {} {} {:?} {:?} {} {} {}",
                    ptr, weak, success_order, fail_order, ops[mem_loc], ops[expected_value], ops[desired_value])
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
            },
            &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])
            },
305

306 307 308
            &Instruction_::Fence(order) => {
                format!("FENCE {:?}", order)
            },
309

310 311
            &Instruction_::Return(ref vals) => format!("RET {}", op_vector_str(vals, ops)),
            &Instruction_::ThreadExit => "THREADEXIT".to_string(),
312
            &Instruction_::Throw(exn_obj) => format!("THROW {}", ops[exn_obj]),
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
            &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)),
qinsoon's avatar
qinsoon committed
332
            &Instruction_::CCall{ref data, ref resume} => format!("CALL {} {}", data.debug_str(ops), resume.debug_str(ops)),
333 334 335 336 337 338 339 340 341 342 343 344 345
            &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("}}");
346

347 348 349 350 351 352
                ret
            },
            &Instruction_::ExnInstruction{ref inner, ref resume} => {
                format!("{} {}", inner.debug_str(ops), resume.debug_str(ops))
            }
        }
353
    }
354 355
}

qinsoon's avatar
qinsoon committed
356
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
357 358 359 360 361 362 363 364 365 366
pub enum MemoryOrder {
    NotAtomic,
    Relaxed,
    Consume,
    Acquire,
    Release,
    AcqRel,
    SeqCst
}

qinsoon's avatar
qinsoon committed
367
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
368 369 370 371 372
pub enum CallConvention {
    Mu,
    Foreign(ForeignFFI)
}

qinsoon's avatar
qinsoon committed
373
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
374 375 376 377
pub enum ForeignFFI {
    C
}

qinsoon's avatar
qinsoon committed
378
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
379 380 381 382 383 384 385 386 387 388 389 390
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))
    }
}

qinsoon's avatar
qinsoon committed
391
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
392 393 394 395 396 397 398 399 400 401 402
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))
    }
}

qinsoon's avatar
qinsoon committed
403
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
404
pub struct Destination {
qinsoon's avatar
qinsoon committed
405
    pub target: MuID,
406 407 408 409 410
    pub args: Vec<DestArg>
}

impl Destination {
    fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
qinsoon's avatar
qinsoon committed
411
        let mut ret = format!("{} with ", self.target);
412 413 414 415 416 417 418 419 420
        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(']');
421

422 423
        ret
    }
424 425 426 427 428 429 430 431 432 433
    
    pub fn get_arguments(&self, ops: &Vec<P<TreeNode>>) -> Vec<P<Value>> {
        vec_utils::map(&self.args, 
            |x| {
                match x {
                    &DestArg::Normal(i) => ops[i].clone_value(),
                    &DestArg::Freshbound(_) => unimplemented!()
                }
        })
    }
434 435
}

qinsoon's avatar
qinsoon committed
436
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
437 438 439 440 441 442 443 444 445
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]),
446
            &DestArg::Freshbound(n) => format!("${}", n)
447 448
        }
    }
qinsoon's avatar
qinsoon committed
449
}