WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.7% of users enabled 2FA.

inst_sel.rs 164 KB
Newer Older
1
use ast::ir::*;
2
use ast::ptr::*;
qinsoon's avatar
qinsoon committed
3
use ast::inst::*;
4
use ast::op;
qinsoon's avatar
qinsoon committed
5
use ast::op::*;
qinsoon's avatar
qinsoon committed
6
use ast::types;
qinsoon's avatar
qinsoon committed
7
use ast::types::*;
qinsoon's avatar
qinsoon committed
8
use vm::VM;
qinsoon's avatar
qinsoon committed
9
use runtime::mm;
10
11
use runtime::mm::objectmodel::OBJECT_HEADER_SIZE;
use runtime::mm::objectmodel::OBJECT_HEADER_OFFSET;
12
13
14
15
use runtime::ValueLocation;
use runtime::thread;
use runtime::entrypoints;
use runtime::entrypoints::RuntimeEntrypoint;
16
17

use compiler::CompilerPass;
18
use compiler::backend;
qinsoon's avatar
qinsoon committed
19
use compiler::backend::PROLOGUE_BLOCK_NAME;
qinsoon's avatar
qinsoon committed
20
21
22
use compiler::backend::x86_64;
use compiler::backend::x86_64::CodeGenerator;
use compiler::backend::x86_64::ASMCodeGen;
qinsoon's avatar
qinsoon committed
23
24
use compiler::machine_code::CompiledFunction;
use compiler::frame::Frame;
25

26
use std::collections::HashMap;
qinsoon's avatar
qinsoon committed
27
use std::any::Any;
28

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
lazy_static! {
    pub static ref LONG_4_TYPE : P<MuType> = P(
        MuType::new(new_internal_id(), MuType_::mustruct(Mu("long_4"), vec![UINT32_TYPE.clone(); 4]))
    );

    pub static ref UITOFP_C0 : P<Value> = P(Value{
        hdr: MuEntityHeader::named(new_internal_id(), Mu("UITOFP_C0")),
        ty : LONG_4_TYPE.clone(),
        v  : Value_::Constant(Constant::List(vec![
            P(Value{
                hdr: MuEntityHeader::unnamed(new_internal_id()),
                ty: UINT32_TYPE.clone(),
                v : Value_::Constant(Constant::Int(1127219200u64))
            }),
            P(Value{
                hdr: MuEntityHeader::unnamed(new_internal_id()),
                ty: UINT32_TYPE.clone(),
                v : Value_::Constant(Constant::Int(1160773632u64))
            }),
            P(Value{
                hdr: MuEntityHeader::unnamed(new_internal_id()),
                ty: UINT32_TYPE.clone(),
                v : Value_::Constant(Constant::Int(0u64))
            }),
            P(Value{
                hdr: MuEntityHeader::unnamed(new_internal_id()),
                ty: UINT32_TYPE.clone(),
                v : Value_::Constant(Constant::Int(0u64))
            })
        ]))
    });

    pub static ref QUAD_2_TYPE : P<MuType> = P(
        MuType::new(new_internal_id(), MuType_::mustruct(Mu("quad_2"), vec![UINT64_TYPE.clone(); 2]))
    );

    pub static ref UITOFP_C1 : P<Value> = P(Value{
        hdr: MuEntityHeader::named(new_internal_id(), Mu("UITOFP_C1")),
        ty : QUAD_2_TYPE.clone(),
        v  : Value_::Constant(Constant::List(vec![
            P(Value{
                hdr: MuEntityHeader::unnamed(new_internal_id()),
                ty: UINT64_TYPE.clone(),
                v : Value_::Constant(Constant::Int(4841369599423283200u64))
            }),
            P(Value{
                hdr: MuEntityHeader::unnamed(new_internal_id()),
                ty: UINT64_TYPE.clone(),
                v : Value_::Constant(Constant::Int(4985484787499139072u64))
            })
        ]))
    });
81
82
83
84
85
86

    pub static ref FPTOUI_C : P<Value> = P(Value{
        hdr: MuEntityHeader::named(new_internal_id(), Mu("FPTOUI_C")),
        ty : UINT64_TYPE.clone(),
        v  : Value_::Constant(Constant::Int(4890909195324358656u64))
    });
87
88
}

89
90
const INLINE_FASTPATH : bool = false;

91
pub struct InstructionSelection {
92
    name: &'static str,
93
    backend: Box<CodeGenerator>,
94
95

    current_fv_id: MuID,
qinsoon's avatar
qinsoon committed
96
    current_callsite_id: usize,
qinsoon's avatar
qinsoon committed
97
98
    current_frame: Option<Frame>,
    current_block: Option<MuName>,
qinsoon's avatar
qinsoon committed
99
100
101
102
    current_func_start: Option<ValueLocation>,
    // key: block id, val: callsite that names the block as exception block
    current_exn_callsites: HashMap<MuID, Vec<ValueLocation>>,
    // key: block id, val: block location
103
104
105
106
    current_exn_blocks: HashMap<MuID, ValueLocation>,

    current_constants: HashMap<MuID, P<Value>>,
    current_constants_locs: HashMap<MuID, P<Value>>
107
108
}

109
impl <'a> InstructionSelection {
qinsoon's avatar
qinsoon committed
110
    #[cfg(feature = "aot")]
111
    pub fn new() -> InstructionSelection {
112
113
        InstructionSelection{
            name: "Instruction Selection (x64)",
114
            backend: Box::new(ASMCodeGen::new()),
115

116
            current_fv_id: 0,
qinsoon's avatar
qinsoon committed
117
            current_callsite_id: 0,
qinsoon's avatar
qinsoon committed
118
119
120
            current_frame: None,
            current_block: None,
            current_func_start: None,
qinsoon's avatar
qinsoon committed
121
122
            // key: block id, val: callsite that names the block as exception block
            current_exn_callsites: HashMap::new(), 
123
124
125
126
            current_exn_blocks: HashMap::new(),

            current_constants: HashMap::new(),
            current_constants_locs: HashMap::new()
127
128
        }
    }
qinsoon's avatar
qinsoon committed
129
130
131
132
133

    #[cfg(feature = "jit")]
    pub fn new() -> InstructionSelection {
        unimplemented!()
    }
134
135
    
    // in this pass, we assume that
qinsoon's avatar
qinsoon committed
136
    // * we do not need to backup/restore caller-saved registers
137
138
    // if any of these assumption breaks, we will need to re-emit the code
    #[allow(unused_variables)]
139
    fn instruction_select(&mut self, node: &'a TreeNode, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) {
140
        trace!("instsel on node#{} {}", node.id(), node);
qinsoon's avatar
qinsoon committed
141
142
        
        match node.v {
143
144
            TreeNode_::Instruction(ref inst) => {
                match inst.v {
qinsoon's avatar
qinsoon committed
145
                    Instruction_::Branch2{cond, ref true_dest, ref false_dest, true_prob} => {
qinsoon's avatar
qinsoon committed
146
                        trace!("instsel on BRANCH2");
147
148
                        // 'branch_if_true' == true, we emit cjmp the same as CmpOp  (je  for EQ, jne for NE)
                        // 'branch_if_true' == false, we emit opposite cjmp as CmpOp (jne for EQ, je  for NE)
149
150
151
152
153
                        let (fallthrough_dest, branch_dest, branch_if_true) = {
                            if true_prob > 0.5f32 {
                                (true_dest, false_dest, false)
                            } else {
                                (false_dest, true_dest, true)
154
                            }
155
                        };
156
                        
qinsoon's avatar
qinsoon committed
157
                        let ops = inst.ops.read().unwrap();
158
                        
159
160
                        self.process_dest(&ops, fallthrough_dest, f_content, f_context, vm);
                        self.process_dest(&ops, branch_dest, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
161
                        
162
                        let branch_target = f_content.get_block(branch_dest.target).name().unwrap();
163
164
165
    
                        let ref cond = ops[cond];
                        
qinsoon's avatar
qinsoon committed
166
                        if self.match_cmp_res(cond) {
167
                            trace!("emit cmp_res-branch2");
168
                            match self.emit_cmp_res(cond, f_content, f_context, vm) {
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
                                op::CmpOp::EQ => {
                                    if branch_if_true {
                                        self.backend.emit_je(branch_target);
                                    } else {
                                        self.backend.emit_jne(branch_target);
                                    }
                                },
                                op::CmpOp::NE => {
                                    if branch_if_true {
                                        self.backend.emit_jne(branch_target);
                                    } else {
                                        self.backend.emit_je(branch_target);
                                    }
                                },
                                op::CmpOp::UGE => {
                                    if branch_if_true {
                                        self.backend.emit_jae(branch_target);
                                    } else {
                                        self.backend.emit_jb(branch_target);
                                    }
                                },
                                op::CmpOp::UGT => {
                                    if branch_if_true {
                                        self.backend.emit_ja(branch_target);
                                    } else {
                                        self.backend.emit_jbe(branch_target);
                                    }
                                },
                                op::CmpOp::ULE => {
                                    if branch_if_true {
                                        self.backend.emit_jbe(branch_target);
                                    } else {
                                        self.backend.emit_ja(branch_target);
                                    }
                                },
                                op::CmpOp::ULT => {
                                    if branch_if_true {
                                        self.backend.emit_jb(branch_target);
                                    } else {
                                        self.backend.emit_jae(branch_target);
                                    }
                                },
                                op::CmpOp::SGE => {
                                    if branch_if_true {
                                        self.backend.emit_jge(branch_target);
                                    } else {
                                        self.backend.emit_jl(branch_target);
                                    }
                                },
                                op::CmpOp::SGT => {
                                    if branch_if_true {
                                        self.backend.emit_jg(branch_target);
                                    } else {
                                        self.backend.emit_jle(branch_target);
                                    }
                                },
                                op::CmpOp::SLE => {
                                    if branch_if_true {
                                        self.backend.emit_jle(branch_target);
                                    } else {
                                        self.backend.emit_jg(branch_target);
                                    }
                                },
                                op::CmpOp::SLT => {
                                    if branch_if_true {
                                        self.backend.emit_jl(branch_target);
                                    } else {
                                        self.backend.emit_jge(branch_target);
                                    }
                                },
qinsoon's avatar
qinsoon committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

                                // floating point

                                op::CmpOp::FOEQ | op::CmpOp::FUEQ => {
                                    if branch_if_true {
                                        self.backend.emit_je(branch_target);
                                    } else {
                                        self.backend.emit_jne(branch_target);
                                    }
                                },
                                op::CmpOp::FONE | op::CmpOp::FUNE => {
                                    if branch_if_true {
                                        self.backend.emit_jne(branch_target);
                                    } else {
                                        self.backend.emit_je(branch_target);
                                    }
                                },
                                op::CmpOp::FOGT | op::CmpOp::FUGT => {
                                    if branch_if_true {
                                        self.backend.emit_ja(branch_target);
                                    } else {
                                        self.backend.emit_jbe(branch_target);
                                    }
                                },
                                op::CmpOp::FOGE | op::CmpOp::FUGE => {
                                    if branch_if_true {
                                        self.backend.emit_jae(branch_target);
                                    } else {
                                        self.backend.emit_jb(branch_target);
                                    }
                                },
                                op::CmpOp::FOLT | op::CmpOp::FULT => {
                                    if branch_if_true {
                                        self.backend.emit_jb(branch_target);
                                    } else {
                                        self.backend.emit_jae(branch_target);
                                    }
                                },
                                op::CmpOp::FOLE | op::CmpOp::FULE => {
                                    if branch_if_true {
                                        self.backend.emit_jbe(branch_target);
                                    } else {
                                        self.backend.emit_ja(branch_target);
                                    }
                                },

qinsoon's avatar
qinsoon committed
285
286
287
288
                                _ => unimplemented!()
                            }
                        } else if self.match_ireg(cond) {
                            trace!("emit ireg-branch2");
289
                            
290
                            let cond_reg = self.emit_ireg(cond, f_content, f_context, vm);
291
                            
qinsoon's avatar
qinsoon committed
292
                            // emit: cmp cond_reg 1
qinsoon's avatar
qinsoon committed
293
                            self.backend.emit_cmp_imm_r(1, &cond_reg);
qinsoon's avatar
qinsoon committed
294
                            // emit: je #branch_dest
295
                            self.backend.emit_je(branch_target);
qinsoon's avatar
qinsoon committed
296
297
                        } else {
                            unimplemented!();
298
                        }
299
                    },
qinsoon's avatar
qinsoon committed
300
301

                    Instruction_::Select{cond, true_val, false_val} => {
qinsoon's avatar
qinsoon committed
302
303
                        use ast::op::CmpOp::*;

qinsoon's avatar
qinsoon committed
304
                        trace!("instsel on SELECT");
qinsoon's avatar
qinsoon committed
305
306
307
308
309
310
311
312
313
                        let ops = inst.ops.read().unwrap();

                        let ref cond = ops[cond];
                        let ref true_val = ops[true_val];
                        let ref false_val = ops[false_val];

                        if self.match_ireg(true_val) {
                            // moving integers/pointers
                            let tmp_res   = self.get_result_value(node);
qinsoon's avatar
qinsoon committed
314

qinsoon's avatar
qinsoon committed
315
316
317
                            // generate compare
                            let cmpop = if self.match_cmp_res(cond) {
                                self.emit_cmp_res(cond, f_content, f_context, vm)
qinsoon's avatar
qinsoon committed
318
319
320
                            } else if self.match_ireg(cond) {
                                let tmp_cond = self.emit_ireg(cond, f_content, f_context, vm);
                                // emit: cmp cond_reg 1
qinsoon's avatar
qinsoon committed
321
                                self.backend.emit_cmp_imm_r(1, &tmp_cond);
qinsoon's avatar
qinsoon committed
322

qinsoon's avatar
qinsoon committed
323
                                EQ
qinsoon's avatar
qinsoon committed
324
                            } else {
qinsoon's avatar
qinsoon committed
325
326
327
                                panic!("expected ireg, found {}", cond)
                            };

328
                            // use cmov for 16/32/64bit integer
qinsoon's avatar
qinsoon committed
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
                            // use jcc  for 8 bit
                            match tmp_res.ty.get_int_length() {
                                // cmov
                                Some(len) if len > 8 => {
                                    let tmp_true  = self.emit_ireg(true_val, f_content, f_context, vm);
                                    let tmp_false = self.emit_ireg(false_val, f_content, f_context, vm);

                                    // mov tmp_false -> tmp_res
                                    self.backend.emit_mov_r_r(&tmp_res, &tmp_false);

                                    match cmpop {
                                        EQ  => self.backend.emit_cmove_r_r (&tmp_res, &tmp_true),
                                        NE  => self.backend.emit_cmovne_r_r(&tmp_res, &tmp_true),
                                        SGE => self.backend.emit_cmovge_r_r(&tmp_res, &tmp_true),
                                        SGT => self.backend.emit_cmovg_r_r (&tmp_res, &tmp_true),
                                        SLE => self.backend.emit_cmovle_r_r(&tmp_res, &tmp_true),
                                        SLT => self.backend.emit_cmovl_r_r (&tmp_res, &tmp_true),
                                        UGE => self.backend.emit_cmovae_r_r(&tmp_res, &tmp_true),
                                        UGT => self.backend.emit_cmova_r_r (&tmp_res, &tmp_true),
                                        ULE => self.backend.emit_cmovbe_r_r(&tmp_res, &tmp_true),
                                        ULT => self.backend.emit_cmovb_r_r (&tmp_res, &tmp_true),

                                        FOEQ | FUEQ => self.backend.emit_cmove_r_r (&tmp_res, &tmp_true),
                                        FONE | FUNE => self.backend.emit_cmovne_r_r(&tmp_res, &tmp_true),
                                        FOGT | FUGT => self.backend.emit_cmova_r_r (&tmp_res, &tmp_true),
                                        FOGE | FUGE => self.backend.emit_cmovae_r_r(&tmp_res, &tmp_true),
                                        FOLT | FULT => self.backend.emit_cmovb_r_r (&tmp_res, &tmp_true),
                                        FOLE | FULE => self.backend.emit_cmovbe_r_r(&tmp_res, &tmp_true),

                                        _ => unimplemented!()
                                    }
                                }
                                // jcc
                                _ => {
363
364
                                    let blk_true  = format!("{}_select_true", node.id());
                                    let blk_false = format!("{}_select_false", node.id());
qinsoon's avatar
qinsoon committed
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
                                    let blk_end   = format!("{}_select_end", node.id());

                                    // jump to blk_true if true
                                    match cmpop {
                                        EQ  => self.backend.emit_je (blk_true.clone()),
                                        NE  => self.backend.emit_jne(blk_true.clone()),
                                        SGE => self.backend.emit_jge(blk_true.clone()),
                                        SGT => self.backend.emit_jg (blk_true.clone()),
                                        SLE => self.backend.emit_jle(blk_true.clone()),
                                        SLT => self.backend.emit_jl (blk_true.clone()),
                                        UGE => self.backend.emit_jae(blk_true.clone()),
                                        UGT => self.backend.emit_ja (blk_true.clone()),
                                        ULE => self.backend.emit_jbe(blk_true.clone()),
                                        ULT => self.backend.emit_jb (blk_true.clone()),

                                        FOEQ | FUEQ => self.backend.emit_je (blk_true.clone()),
                                        FONE | FUNE => self.backend.emit_jne(blk_true.clone()),
                                        FOGT | FUGT => self.backend.emit_ja (blk_true.clone()),
                                        FOGE | FUGE => self.backend.emit_jae(blk_true.clone()),
                                        FOLT | FULT => self.backend.emit_jb (blk_true.clone()),
                                        FOLE | FULE => self.backend.emit_jbe(blk_true.clone()),

                                        _ => unimplemented!()
                                    }

390
391
392
393
394
395
396
397
                                    // finishing current block
                                    let cur_block = self.current_block.as_ref().unwrap().clone();
                                    self.backend.end_block(cur_block.clone());

                                    // blk_false:
                                    self.current_block = Some(blk_false.clone());
                                    self.backend.start_block(blk_false.clone());

qinsoon's avatar
qinsoon committed
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
                                    // mov false result here
                                    self.emit_move_node_to_value(&tmp_res, &false_val, f_content, f_context, vm);

                                    // jmp to end
                                    self.backend.emit_jmp(blk_end.clone());

                                    // finishing current block
                                    let cur_block = self.current_block.as_ref().unwrap().clone();
                                    self.backend.end_block(cur_block.clone());

                                    // blk_true:
                                    self.current_block = Some(blk_true.clone());
                                    self.backend.start_block(blk_true.clone());
                                    // mov true value -> result
                                    self.emit_move_node_to_value(&tmp_res, &true_val, f_content, f_context, vm);

                                    self.backend.end_block(blk_true.clone());

                                    // blk_end:
                                    self.backend.start_block(blk_end.clone());
                                    self.current_block = Some(blk_end.clone());
                                }
qinsoon's avatar
qinsoon committed
420
421
422
423
424
425
426
                            }
                        } else {
                            // moving vectors, floatingpoints
                            unimplemented!()
                        }
                    },

427
                    Instruction_::CmpOp(op, op1, op2) => {
qinsoon's avatar
qinsoon committed
428
429
                        use ast::op::CmpOp::*;

qinsoon's avatar
qinsoon committed
430
                        trace!("instsel on CMPOP");
431
432
433
434
                        let ops = inst.ops.read().unwrap();
                        let ref op1 = ops[op1];
                        let ref op2 = ops[op2];

qinsoon's avatar
qinsoon committed
435
                        let tmp_res = self.get_result_value(node);
436
437
438
                        
                        debug_assert!(tmp_res.ty.get_int_length().is_some());
                        debug_assert!(tmp_res.ty.get_int_length().unwrap() == 1);
qinsoon's avatar
qinsoon committed
439

440
                        // set byte to result
qinsoon's avatar
qinsoon committed
441
                        match self.emit_cmp_res(node, f_content, f_context, vm) {
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
                            EQ  => self.backend.emit_sete_r (&tmp_res),
                            NE  => self.backend.emit_setne_r(&tmp_res),
                            SGE => self.backend.emit_setge_r(&tmp_res),
                            SGT => self.backend.emit_setg_r (&tmp_res),
                            SLE => self.backend.emit_setle_r(&tmp_res),
                            SLT => self.backend.emit_setl_r (&tmp_res),
                            UGE => self.backend.emit_setae_r(&tmp_res),
                            UGT => self.backend.emit_seta_r (&tmp_res),
                            ULE => self.backend.emit_setbe_r(&tmp_res),
                            ULT => self.backend.emit_setb_r (&tmp_res),

                            FOEQ | FUEQ => self.backend.emit_sete_r (&tmp_res),
                            FONE | FUNE => self.backend.emit_setne_r(&tmp_res),
                            FOGT | FUGT => self.backend.emit_seta_r (&tmp_res),
                            FOGE | FUGE => self.backend.emit_setae_r(&tmp_res),
                            FOLT | FULT => self.backend.emit_setb_r (&tmp_res),
                            FOLE | FULE => self.backend.emit_setbe_r(&tmp_res),
qinsoon's avatar
qinsoon committed
459
460

                            _ => unimplemented!()
461
462
463
                        }
                    }

qinsoon's avatar
qinsoon committed
464
                    Instruction_::Branch1(ref dest) => {
qinsoon's avatar
qinsoon committed
465
                        trace!("instsel on BRANCH1");
qinsoon's avatar
qinsoon committed
466
                        let ops = inst.ops.read().unwrap();
467
                                            
468
                        self.process_dest(&ops, dest, f_content, f_context, vm);
469
                        
470
                        let target = f_content.get_block(dest.target).name().unwrap();
qinsoon's avatar
qinsoon committed
471
                        
qinsoon's avatar
qinsoon committed
472
                        trace!("emit branch1");
473
                        // jmp
qinsoon's avatar
qinsoon committed
474
                        self.backend.emit_jmp(target);
475
                    },
qinsoon's avatar
qinsoon committed
476
477

                    Instruction_::Switch{cond, ref default, ref branches} => {
qinsoon's avatar
qinsoon committed
478
                        trace!("instsel on SWITCH");
qinsoon's avatar
qinsoon committed
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
                        let ops = inst.ops.read().unwrap();

                        let ref cond = ops[cond];

                        if self.match_ireg(cond) {
                            let tmp_cond = self.emit_ireg(cond, f_content, f_context, vm);

                            // emit each branch
                            for &(case_op_index, ref case_dest) in branches {
                                let ref case_op = ops[case_op_index];

                                // process dest
                                self.process_dest(&ops, case_dest, f_content, f_context, vm);

                                let target = f_content.get_block(case_dest.target).name().unwrap();

                                if self.match_iimm(case_op) {
                                    let imm = self.node_iimm_to_i32(case_op);

                                    // cmp case cond
qinsoon's avatar
qinsoon committed
499
                                    self.backend.emit_cmp_imm_r(imm, &tmp_cond);
qinsoon's avatar
qinsoon committed
500
501
502
503
504
505
                                    // je dest
                                    self.backend.emit_je(target);
                                } else if self.match_ireg(case_op) {
                                    let tmp_case_op = self.emit_ireg(case_op, f_content, f_context, vm);

                                    // cmp case cond
qinsoon's avatar
qinsoon committed
506
                                    self.backend.emit_cmp_r_r(&tmp_case_op, &tmp_cond);
qinsoon's avatar
qinsoon committed
507
508
509
510
511
                                    // je dest
                                    self.backend.emit_je(target);
                                } else {
                                    panic!("expecting ireg cond to be either iimm or ireg: {}", cond);
                                }
512
513
514

                                self.finish_block();
                                self.start_block(format!("{}_switch_not_met_case_{}", node.id(), case_op_index));
qinsoon's avatar
qinsoon committed
515
516
517
518
519
520
521
522
523
524
525
                            }

                            // emit default
                            self.process_dest(&ops, default, f_content, f_context, vm);
                            
                            let default_target = f_content.get_block(default.target).name().unwrap();
                            self.backend.emit_jmp(default_target);
                        } else {
                            panic!("expecting cond in switch to be ireg: {}", cond);
                        }
                    }
526
                    
qinsoon's avatar
qinsoon committed
527
                    Instruction_::ExprCall{ref data, is_abort} => {
qinsoon's avatar
qinsoon committed
528
529
                        trace!("instsel on EXPRCALL");

qinsoon's avatar
qinsoon committed
530
531
                        if is_abort {
                            unimplemented!()
532
                        }
533
                        
qinsoon's avatar
qinsoon committed
534
535
536
537
538
                        self.emit_mu_call(
                            inst, // inst: &Instruction,
                            data, // calldata: &CallData,
                            None, // resumption: Option<&ResumptionData>,
                            node, // cur_node: &TreeNode, 
539
                            f_content, f_context, vm);
540
541
                    },
                    
qinsoon's avatar
qinsoon committed
542
                    Instruction_::Call{ref data, ref resume} => {
qinsoon's avatar
qinsoon committed
543
544
                        trace!("instsel on CALL");

qinsoon's avatar
qinsoon committed
545
546
547
548
549
550
                        self.emit_mu_call(
                            inst, 
                            data, 
                            Some(resume), 
                            node, 
                            f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
551
552
553
                    },

                    Instruction_::ExprCCall{ref data, is_abort} => {
qinsoon's avatar
qinsoon committed
554
555
                        trace!("instsel on EXPRCCALL");

qinsoon's avatar
qinsoon committed
556
557
558
559
560
561
562
563
                        if is_abort {
                            unimplemented!()
                        }

                        self.emit_c_call_ir(inst, data, None, node, f_content, f_context, vm);
                    }

                    Instruction_::CCall{ref data, ref resume} => {
qinsoon's avatar
qinsoon committed
564
565
                        trace!("instsel on CCALL");

qinsoon's avatar
qinsoon committed
566
                        self.emit_c_call_ir(inst, data, Some(resume), node, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
567
568
                    }
                    
569
                    Instruction_::Return(_) => {
qinsoon's avatar
qinsoon committed
570
571
                        trace!("instsel on RETURN");

572
                        self.emit_common_epilogue(inst, f_content, f_context, vm);
573
                        
qinsoon's avatar
qinsoon committed
574
                        self.backend.emit_ret();
575
576
                    },
                    
qinsoon's avatar
qinsoon committed
577
                    Instruction_::BinOp(op, op1, op2) => {
qinsoon's avatar
qinsoon committed
578
579
                        trace!("instsel on BINOP");

qinsoon's avatar
qinsoon committed
580
581
                        self.emit_binop(node, inst, op, op1, op2, f_content, f_context, vm);
                    },
qinsoon's avatar
qinsoon committed
582

qinsoon's avatar
qinsoon committed
583
584
                    Instruction_::BinOpWithStatus(op, status, op1, op2) => {
                        trace!("instsel on BINOP_STATUS");
qinsoon's avatar
qinsoon committed
585

qinsoon's avatar
qinsoon committed
586
                        self.emit_binop(node, inst, op, op1, op2, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
587

qinsoon's avatar
qinsoon committed
588
589
                        let values = inst.value.as_ref().unwrap();
                        let mut status_value_index = 1;
qinsoon's avatar
qinsoon committed
590

qinsoon's avatar
qinsoon committed
591
592
593
594
                        // negative flag
                        if status.flag_n {
                            let tmp_status = values[status_value_index].clone();
                            status_value_index += 1;
qinsoon's avatar
qinsoon committed
595

qinsoon's avatar
qinsoon committed
596
597
                            self.backend.emit_sets_r8(&tmp_status);
                        }
qinsoon's avatar
qinsoon committed
598

qinsoon's avatar
qinsoon committed
599
600
601
602
                        // zero flag
                        if status.flag_z {
                            let tmp_status = values[status_value_index].clone();
                            status_value_index += 1;
qinsoon's avatar
qinsoon committed
603

qinsoon's avatar
qinsoon committed
604
605
                            self.backend.emit_setz_r8(&tmp_status);
                        }
qinsoon's avatar
qinsoon committed
606

qinsoon's avatar
qinsoon committed
607
608
609
610
                        // unsigned overflow
                        if status.flag_c {
                            let tmp_status = values[status_value_index].clone();
                            status_value_index += 1;
qinsoon's avatar
qinsoon committed
611

qinsoon's avatar
qinsoon committed
612
613
614
                            match op {
                                BinOp::Add | BinOp::Sub | BinOp::Mul => {
                                    self.backend.emit_setb_r8(&tmp_status);
qinsoon's avatar
qinsoon committed
615
                                }
qinsoon's avatar
qinsoon committed
616
                                _ => panic!("Only Add/Sub/Mul has #C flag")
qinsoon's avatar
qinsoon committed
617
                            }
qinsoon's avatar
qinsoon committed
618
                        }
qinsoon's avatar
qinsoon committed
619

qinsoon's avatar
qinsoon committed
620
621
622
                        // signed overflow
                        if status.flag_v {
                            let tmp_status = values[status_value_index].clone();
qinsoon's avatar
qinsoon committed
623

qinsoon's avatar
qinsoon committed
624
625
626
                            match op {
                                BinOp::Add | BinOp::Sub | BinOp::Mul => {
                                    self.backend.emit_seto_r8(&tmp_status);
qinsoon's avatar
qinsoon committed
627
                                }
qinsoon's avatar
qinsoon committed
628
                                _ => panic!("Only Add/Sub/Mul has #V flag")
qinsoon's avatar
qinsoon committed
629
                            }
630
631
                        }
                    }
qinsoon's avatar
qinsoon committed
632
633

                    Instruction_::ConvOp{operation, ref from_ty, ref to_ty, operand} => {
qinsoon's avatar
qinsoon committed
634
635
                        trace!("instsel on CONVOP");

qinsoon's avatar
qinsoon committed
636
637
638
639
640
641
642
643
                        let ops = inst.ops.read().unwrap();

                        let ref op = ops[operand];

                        match operation {
                            op::ConvOp::TRUNC => {
                                if self.match_ireg(op) {
                                    let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
644

qinsoon's avatar
qinsoon committed
645
                                    let tmp_res = self.get_result_value(node);
646
647
648
649
                                    let dst_length = match tmp_res.ty.get_int_length() {
                                        Some(len) => len,
                                        None => panic!("dst of TRUNC is not int: {}", tmp_res)
                                    };
qinsoon's avatar
qinsoon committed
650
651

                                    // mov op -> result
652
653
654
655
656
657
658
659
                                    match dst_length {
                                        1  => self.backend.emit_mov_r_r(&tmp_res, unsafe {&tmp_op.as_type(UINT8_TYPE.clone())}),
                                        8  => self.backend.emit_mov_r_r(&tmp_res, unsafe {&tmp_op.as_type(UINT8_TYPE.clone())}),
                                        16 => self.backend.emit_mov_r_r(&tmp_res, unsafe {&tmp_op.as_type(UINT16_TYPE.clone())}),
                                        32 => self.backend.emit_mov_r_r(&tmp_res, unsafe {&tmp_op.as_type(UINT32_TYPE.clone())}),
                                        64 => self.backend.emit_mov_r_r(&tmp_res, unsafe {&tmp_op.as_type(UINT64_TYPE.clone())}),
                                        _  => panic!("unsupported int length: {}", dst_length)
                                    }
qinsoon's avatar
qinsoon committed
660
661
662
663
664
                                } else {
                                    panic!("unexpected op (expect ireg): {}", op);
                                }
                            }
                            op::ConvOp::ZEXT => {
665
666
667
                                if self.match_ireg(op) {
                                    let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
                                    let tmp_res = self.get_result_value(node);
qinsoon's avatar
qinsoon committed
668

669
670
671
                                    // movz op -> result
                                    let from_ty_size = vm.get_backend_type_info(from_ty.id()).size;
                                    let to_ty_size   = vm.get_backend_type_info(to_ty.id()).size;
qinsoon's avatar
qinsoon committed
672

673
                                    if from_ty_size != to_ty_size {
qinsoon's avatar
qinsoon committed
674
675
676
677
678
679
680
681
682
683
684
685
686
                                        if from_ty_size == 4 && to_ty_size == 8 {
                                            // zero extend from 32 bits to 64 bits is a mov instruction
                                            // x86 does not have movzlq (32 to 64)

                                            // tmp_op is int32, but tmp_res is int64
                                            // we want to force a 32-to-32 mov, so high bits of the destination will be zeroed

                                            let tmp_res32 = unsafe {tmp_res.as_type(UINT32_TYPE.clone())};

                                            self.backend.emit_mov_r_r(&tmp_res32, &tmp_op);
                                        } else {
                                            self.backend.emit_movz_r_r(&tmp_res, &tmp_op);
                                        }
qinsoon's avatar
qinsoon committed
687
                                    } else {
688
                                        self.backend.emit_mov_r_r(&tmp_res, &tmp_op);
qinsoon's avatar
qinsoon committed
689
                                    }
690
691
                                } else {
                                    panic!("unexpected op (expect ireg): {}", op);
qinsoon's avatar
qinsoon committed
692
693
694
                                }
                            },
                            op::ConvOp::SEXT => {
695
696
697
                                if self.match_ireg(op) {
                                    let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
                                    let tmp_res = self.get_result_value(node);
qinsoon's avatar
qinsoon committed
698

699
700
701
                                    // movs op -> result
                                    let from_ty_size = vm.get_backend_type_info(from_ty.id()).size;
                                    let to_ty_size   = vm.get_backend_type_info(to_ty.id()).size;
qinsoon's avatar
qinsoon committed
702

703
                                    if from_ty_size != to_ty_size {
qinsoon's avatar
qinsoon committed
704
705
                                        self.backend.emit_movs_r_r(&tmp_res, &tmp_op);
                                    } else {
706
                                        self.backend.emit_mov_r_r(&tmp_res, &tmp_op);
qinsoon's avatar
qinsoon committed
707
                                    }
708
709
                                } else {
                                    panic!("unexpected op (expect ireg): {}", op)
qinsoon's avatar
qinsoon committed
710
711
                                }
                            }
qinsoon's avatar
qinsoon committed
712
713
714
715
716
717
                            op::ConvOp::REFCAST | op::ConvOp::PTRCAST => {
                                // just a mov (and hopefully reg alloc will coalesce it)
                                let tmp_res = self.get_result_value(node);

                                if self.match_ireg(op) {
                                    let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
718
                                    self.backend.emit_mov_r_r(&tmp_res, &tmp_op);
qinsoon's avatar
qinsoon committed
719
720
721
722
                                } else {
                                    panic!("unexpected op (expect ireg): {}", op)
                                }
                            }
qinsoon's avatar
qinsoon committed
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
                            op::ConvOp::SITOFP => {
                                let tmp_res = self.get_result_value(node);

                                if self.match_ireg(op) {
                                    let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
                                    self.backend.emit_cvtsi2sd_f64_r(&tmp_res, &tmp_op);
                                } else {
                                    panic!("unexpected op (expected ireg): {}", op)
                                }
                            }
                            op::ConvOp::FPTOSI => {
                                let tmp_res = self.get_result_value(node);

                                if self.match_fpreg(op) {
                                    let tmp_op = self.emit_fpreg(op, f_content, f_context, vm);
                                    self.backend.emit_cvtsd2si_r_f64(&tmp_res, &tmp_op);
                                } else {
                                    panic!("unexpected op (expected fpreg): {}", op)
                                }
                            }
743
744
745
746
747
748
                            op::ConvOp::UITOFP => {
                                let tmp_res = self.get_result_value(node);

                                if self.match_ireg(op) {
                                    let tmp_op = self.emit_ireg(op, f_content, f_context, vm);

749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
                                    let op_ty_size = vm.get_backend_type_info(tmp_op.ty.id()).size;

                                    match op_ty_size {
                                        8 => {
                                            // movd/movq op -> res
                                            self.backend.emit_mov_fpr_r64(&tmp_res, &tmp_op);

                                            // punpckldq UITOFP_C0, tmp_res -> tmp_res
                                            // (interleaving low bytes: xmm = xmm[0] mem[0] xmm[1] mem[1]
                                            let mem_c0 = self.get_mem_for_const(UITOFP_C0.clone(), vm);
                                            self.backend.emit_punpckldq_f64_mem128(&tmp_res, &mem_c0);

                                            // subpd UITOFP_C1, tmp_res -> tmp_res
                                            let mem_c1 = self.get_mem_for_const(UITOFP_C1.clone(), vm);
                                            self.backend.emit_subpd_f64_mem128(&tmp_res, &mem_c1);

                                            // haddpd tmp_res, tmp_res -> tmp_res
                                            self.backend.emit_haddpd_f64_f64(&tmp_res, &tmp_res);
                                        }
                                        4 => {
                                            let tmp = self.make_temporary(f_context, UINT64_TYPE.clone(), vm);

                                            // movl op -> tmp(32)
                                            let tmp32 = unsafe {tmp.as_type(UINT32_TYPE.clone())};
                                            self.backend.emit_mov_r_r(&tmp32, &tmp_op);

                                            // cvtsi2sd %tmp(64) -> %tmp_res
                                            self.backend.emit_cvtsi2sd_f64_r(&tmp_res, &tmp);
                                        }
                                        2 | 1 => {
                                            let tmp_op32 = unsafe {tmp_op.as_type(UINT32_TYPE.clone())};
                                            self.backend.emit_cvtsi2sd_f64_r(&tmp_res, &tmp_op32);
                                        }
                                        _ => panic!("not implemented int length {}", op_ty_size)
                                    }
                                } else {
                                    panic!("unexpected op (expected ireg): {}", op)
                                }
                            }
                            op::ConvOp::FPTOUI => {
                                let tmp_res = self.get_result_value(node);

                                let res_ty_size = vm.get_backend_type_info(tmp_res.ty.id()).size;
792

793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
                                if self.match_fpreg(op) {
                                    let tmp_op = self.emit_fpreg(op, f_content, f_context, vm);

                                    match res_ty_size {
                                        8 => {
                                            let tmp1 = self.make_temporary(f_context, DOUBLE_TYPE.clone(), vm);
                                            let tmp2 = self.make_temporary(f_context, DOUBLE_TYPE.clone(), vm);

                                            // movsd FPTOUI_C -> %tmp1
                                            let mem_c = self.get_mem_for_const(FPTOUI_C.clone(), vm);
                                            self.backend.emit_movsd_f64_mem64(&tmp1, &mem_c);

                                            // movapd %tmp_op -> %tmp2
                                            self.backend.emit_movapd_f64_f64(&tmp2, &tmp_op);

                                            // subsd %tmp1, %tmp2 -> %tmp2
                                            self.backend.emit_subsd_f64_f64(&tmp2, &tmp1);

                                            // cvttsd2si %tmp2 -> %tmp_res
                                            self.backend.emit_cvttsd2si_r_f64(&tmp_res, &tmp2);

                                            let tmp_const = self.make_temporary(f_context, UINT64_TYPE.clone(), vm);
                                            // mov 0x8000000000000000 -> %tmp_const
                                            self.backend.emit_mov_r64_imm64(&tmp_const, -9223372036854775808i64);

                                            // xor %tmp_res, %tmp_const -> %tmp_const
                                            self.backend.emit_xor_r_r(&tmp_const, &tmp_res);
qinsoon's avatar
qinsoon committed
820

821
822
                                            // cvttsd2si %tmp_op -> %tmp_res
                                            self.backend.emit_cvttsd2si_r_f64(&tmp_res, &tmp_op);
823

824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
                                            // ucomisd %tmp_op %tmp1
                                            self.backend.emit_ucomisd_f64_f64(&tmp1, &tmp_op);

                                            // cmovaeq %tmp_const -> %tmp_res
                                            self.backend.emit_cmovae_r_r(&tmp_res, &tmp_const);
                                        }
                                        4 => {
                                            let tmp_res64 = unsafe {tmp_res.as_type(UINT64_TYPE.clone())};

                                            // cvttsd2si %tmp_op -> %tmp_res(64)
                                            self.backend.emit_cvttsd2si_r_f64(&tmp_res64, &tmp_op);
                                        }
                                        2 | 1 => {
                                            let tmp_res32 = unsafe {tmp_res.as_type(UINT32_TYPE.clone())};

                                            // cvttsd2si %tmp_op -> %tmp_res(32)
                                            self.backend.emit_cvttsd2si_r_f64(&tmp_res32, &tmp_op);

                                            // movz %tmp_res -> %tmp_res(32)
                                            self.backend.emit_movz_r_r(&tmp_res32, &tmp_res);
                                        }
                                        _ => panic!("not implemented int length {}", res_ty_size)
                                    }
847
848
849
850
                                } else {
                                    panic!("unexpected op (expected ireg): {}", op)
                                }
                            }
qinsoon's avatar
qinsoon committed
851
852
853
                            _ => unimplemented!()
                        }
                    }
854
                    
855
856
                    // load on x64 generates mov inst (no matter what order is specified)
                    // https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
857
                    Instruction_::Load{is_ptr, order, mem_loc} => {
qinsoon's avatar
qinsoon committed
858
859
                        trace!("instsel on LOAD");

qinsoon's avatar
qinsoon committed
860
                        let ops = inst.ops.read().unwrap();
861
                        let ref loc_op = ops[mem_loc];
862
863
864
865
866
867
                        
                        // check order
                        match order {
                            MemoryOrder::Relaxed 
                            | MemoryOrder::Consume 
                            | MemoryOrder::Acquire
868
869
                            | MemoryOrder::SeqCst
                            | MemoryOrder::NotAtomic => {},
870
871
                            _ => panic!("didnt expect order {:?} with store inst", order)
                        }                        
872

873
                        let resolved_loc = self.emit_node_addr_to_value(loc_op, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
874
                        let res_temp = self.get_result_value(node);
875
876
877
                        
                        if self.match_ireg(node) {
                            // emit mov(GPR)
qinsoon's avatar
qinsoon committed
878
                            self.backend.emit_mov_r_mem(&res_temp, &resolved_loc);
879
880
                        } else {
                            // emit mov(FPR)
qinsoon's avatar
qinsoon committed
881
                            self.backend.emit_movsd_f64_mem64(&res_temp, &resolved_loc);
882
883
884
885
                        }
                    }
                    
                    Instruction_::Store{is_ptr, order, mem_loc, value} => {
qinsoon's avatar
qinsoon committed
886
887
                        trace!("instsel on STORE");

qinsoon's avatar
qinsoon committed
888
                        let ops = inst.ops.read().unwrap();
889
890
891
892
893
                        let ref loc_op = ops[mem_loc];
                        let ref val_op = ops[value];
                        
                        let generate_plain_mov : bool = {
                            match order {
894
895
896
                                MemoryOrder::Relaxed
                                | MemoryOrder::Release
                                | MemoryOrder::NotAtomic => true,
897
898
899
900
901
                                MemoryOrder::SeqCst => false,
                                _ => panic!("didnt expect order {:?} with store inst", order)
                            }
                        };
                        
902
903
904
905
                        let resolved_loc = self.emit_node_addr_to_value(loc_op, f_content, f_context, vm);

                        if self.match_iimm(val_op) {
                            let val = self.node_iimm_to_i32(val_op);
906
                            if generate_plain_mov {
qinsoon's avatar
qinsoon committed
907
                                self.backend.emit_mov_mem_imm(&resolved_loc, val);
908
909
910
                            } else {
                                unimplemented!()
                            }
911
912
                        } else if self.match_ireg(val_op) {
                            let val = self.emit_ireg(val_op, f_content, f_context, vm);
913
                            if generate_plain_mov {
qinsoon's avatar
qinsoon committed
914
                                self.backend.emit_mov_mem_r(&resolved_loc, &val);
915
916
917
                            } else {
                                unimplemented!()
                            }
918
                        } else {
qinsoon's avatar
qinsoon committed
919
920
                            let val = self.emit_fpreg(val_op, f_content, f_context, vm);

921
                            // emit mov(FPR)
qinsoon's avatar
qinsoon committed
922
                            self.backend.emit_movsd_mem64_f64(&resolved_loc, &val);
923
924
                        }
                    }
qinsoon's avatar
qinsoon committed
925

926
927
928
929
930
                    // memory insts: calculate the address, then lea
                    Instruction_::GetIRef(_)
                    | Instruction_::GetFieldIRef{..}
                    | Instruction_::GetVarPartIRef{..}
                    | Instruction_::ShiftIRef{..} => {
qinsoon's avatar
qinsoon committed
931
932
                        trace!("instsel on GET/FIELD/VARPARTIREF, SHIFTIREF");

933
934
                        let mem_addr = self.emit_get_mem_from_inst(node, f_content, f_context, vm);
                        let tmp_res  = self.get_result_value(node);
qinsoon's avatar
qinsoon committed
935

936
                        self.backend.emit_lea_r64(&tmp_res, &mem_addr);
qinsoon's avatar
qinsoon committed
937
                    }
938
                    
939
                    Instruction_::ThreadExit => {
qinsoon's avatar
qinsoon committed
940
                        trace!("instsel on THREADEXIT");
941
                        // emit a call to swap_back_to_native_stack(sp_loc: Address)
942
943
                        
                        // get thread local and add offset to get sp_loc
qinsoon's avatar
qinsoon committed
944
                        let tl = self.emit_get_threadlocal(Some(node), f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
945
                        self.backend.emit_add_r_imm(&tl, *thread::NATIVE_SP_LOC_OFFSET as i32);
946
                        
qinsoon's avatar
qinsoon committed
947
                        self.emit_runtime_entry(&entrypoints::SWAP_BACK_TO_NATIVE_STACK, vec![tl.clone()], None, Some(node), f_content, f_context, vm);
948
                    }
949
950

                    Instruction_::CommonInst_GetThreadLocal => {
qinsoon's avatar
qinsoon committed
951
                        trace!("instsel on GETTHREADLOCAL");
952
953
954
955
956
957
958
959
960
                        // get thread local
                        let tl = self.emit_get_threadlocal(Some(node), f_content, f_context, vm);

                        let tmp_res = self.get_result_value(node);

                        // load [tl + USER_TLS_OFFSET] -> tmp_res
                        self.emit_load_base_offset(&tmp_res, &tl, *thread::USER_TLS_OFFSET as i32, vm);
                    }
                    Instruction_::CommonInst_SetThreadLocal(op) => {
qinsoon's avatar
qinsoon committed
961
962
                        trace!("instsel on SETTHREADLOCAL");

963
964
965
966
967
968
969
970
971
972
973
974
975
                        let ops = inst.ops.read().unwrap();
                        let ref op = ops[op];

                        debug_assert!(self.match_ireg(op));

                        let tmp_op = self.emit_ireg(op, f_content, f_context, vm);

                        // get thread local
                        let tl = self.emit_get_threadlocal(Some(node), f_content, f_context, vm);

                        // store tmp_op -> [tl + USER_TLS_OFFSTE]
                        self.emit_store_base_offset(&tl, *thread::USER_TLS_OFFSET as i32, &tmp_op, vm);
                    }
976

qinsoon's avatar
qinsoon committed
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
                    Instruction_::CommonInst_Pin(op) => {
                        trace!("instsel on PIN");

                        if !mm::GC_MOVES_OBJECT {
                            // non-moving GC: pin is a nop (move from op to result)
                            let ops = inst.ops.read().unwrap();
                            let ref op = ops[op];

                            let tmp_res = self.get_result_value(node);

                            self.emit_move_node_to_value(&tmp_res, op, f_content, f_context, vm);
                        } else {
                            unimplemented!()
                        }
                    }
                    Instruction_::CommonInst_Unpin(_) => {
                        trace!("instsel on UNPIN");

                        if !mm::GC_MOVES_OBJECT {
                            // do nothing
                        } else {
                            unimplemented!()
                        }
                    }


1003
                    Instruction_::Move(op) => {
qinsoon's avatar
qinsoon committed
1004
1005
                        trace!("instsel on MOVE (internal IR)");

1006
1007
1008
1009
1010
1011
1012
                        let ops = inst.ops.read().unwrap();
                        let ref op = ops[op];

                        let tmp_res = self.get_result_value(node);

                        self.emit_move_node_to_value(&tmp_res, op, f_content, f_context, vm);
                    }
qinsoon's avatar
qinsoon committed
1013
1014
                    
                    Instruction_::New(ref ty) => {
qinsoon's avatar
qinsoon committed
1015
1016
                        trace!("instsel on NEW");

qinsoon's avatar
qinsoon committed
1017
1018
1019
1020
1021
1022
1023
                        if cfg!(debug_assertions) {
                            match ty.v {
                                MuType_::Hybrid(_) => panic!("cannot use NEW for hybrid, use NEWHYBRID instead"),
                                _ => {}
                            }
                        }

qinsoon's avatar
qinsoon committed
1024
                        let ty_info = vm.get_backend_type_info(ty.id());
qinsoon's avatar
qinsoon committed
1025
                        let size = ty_info.size;
1026
                        let ty_align= ty_info.alignment;
qinsoon's avatar
qinsoon committed
1027
1028

                        let const_size = self.make_value_int_const(size as u64, vm);
qinsoon's avatar
qinsoon committed
1029
1030

                        let tmp_allocator = self.emit_get_allocator(node, f_content, f_context, vm);
1031
                        let tmp_res = self.emit_alloc_sequence(tmp_allocator.clone(), const_size, ty_align, node, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
1032
1033
1034
1035
1036
1037
1038
1039
1040

                        // ASM: call muentry_init_object(%allocator, %tmp_res, %encode)
                        let encode = self.make_value_int_const(mm::get_gc_type_encode(ty_info.gc_type.id), vm);
                        self.emit_runtime_entry(
                            &entrypoints::INIT_OBJ,
                            vec![tmp_allocator.clone(), tmp_res.clone(), encode],
                            None,
                            Some(node), f_content, f_context, vm
                        );
qinsoon's avatar
qinsoon committed
1041
1042
1043
                    }

                    Instruction_::NewHybrid(ref ty, var_len) => {
qinsoon's avatar
qinsoon committed
1044
1045
                        trace!("instsel on NEWHYBRID");

qinsoon's avatar
qinsoon committed
1046
1047
1048
1049
1050
                        if cfg!(debug_assertions) {
                            match ty.v {
                                MuType_::Hybrid(_) => {},
                                _ => panic!("NEWHYBRID is only for allocating hybrid types, use NEW for others")
                            }
qinsoon's avatar
qinsoon committed
1051
                        }
qinsoon's avatar
qinsoon committed
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067

                        let ty_info = vm.get_backend_type_info(ty.id());
                        let ty_align = ty_info.alignment;
                        let fix_part_size = ty_info.size;
                        let var_ty_size = match ty.v {
                            MuType_::Hybrid(ref name) => {
                                let map_lock = HYBRID_TAG_MAP.read().unwrap();
                                let hybrid_ty_ = map_lock.get(name).unwrap();
                                let var_ty = hybrid_ty_.get_var_ty();

                                vm.get_backend_type_info(var_ty.id()).size
                            },
                            _ => panic!("only expect HYBRID type here")
                        };

                        // actual size = fix_part_size + var_ty_size * len
qinsoon's avatar
qinsoon committed
1068
                        let (actual_size, length) = {
qinsoon's avatar
qinsoon committed
1069
1070
1071
1072
1073
1074
1075
                            let ops = inst.ops.read().unwrap();
                            let ref var_len = ops[var_len];

                            if self.match_iimm(var_len) {
                                let var_len = self.node_iimm_to_i32(var_len);
                                let actual_size = fix_part_size + var_ty_size * (var_len as usize);

qinsoon's avatar
qinsoon committed
1076
1077
1078
1079
                                (
                                    self.make_value_int_const(actual_size as u64, vm),
                                    self.make_value_int_const(var_len as u64, vm)
                                )
qinsoon's avatar
qinsoon committed
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
                            } else {
                                let tmp_actual_size = self.make_temporary(f_context, UINT64_TYPE.clone(), vm);
                                let tmp_var_len = self.emit_ireg(var_len, f_content, f_context, vm);

                                let is_power_of_two = |x: usize| {
                                    use std::i8;

                                    let mut power_of_two = 1;
                                    let mut i: i8 = 0;
                                    while power_of_two < x && i < i8::MAX {
                                        power_of_two *= 2;
                                        i += 1;
                                    }

                                    if power_of_two == x {
                                        Some(i)
                                    } else {
                                        None
                                    }
                                };

                                match is_power_of_two(var_ty_size) {
                                    Some(shift) => {
1103
1104
1105
                                        // use tmp_actual_size as result - we do not want to change tmp_var_len
                                        self.backend.emit_mov_r_r(&tmp_actual_size, &tmp_var_len);

qinsoon's avatar
qinsoon committed
1106
1107
                                        if shift != 0 {
                                            // a shift-left will get the total size of var part
1108
                                            self.backend.emit_shl_r_imm8(&tmp_actual_size, shift);
qinsoon's avatar
qinsoon committed
1109
                                        }
qinsoon's avatar
qinsoon committed
1110
1111

                                        // add with fix-part size
1112
                                        self.backend.emit_add_r_imm(&tmp_actual_size, fix_part_size as i32);
qinsoon's avatar
qinsoon committed
1113
1114
1115
1116
1117
                                    }
                                    None => {
                                        // we need to do a multiply

                                        // mov var_ty_size -> rax
qinsoon's avatar
qinsoon committed
1118
                                        self.backend.emit_mov_r_imm(&x86_64::RAX, var_ty_size as i32);
qinsoon's avatar
qinsoon committed
1119
1120

                                        // mul tmp_var_len, rax -> rdx:rax
qinsoon's avatar
qinsoon committed
1121
                                        self.backend.emit_mul_r(&tmp_var_len);
qinsoon's avatar
qinsoon committed
1122
1123

                                        // add with fix-part size
qinsoon's avatar
qinsoon committed
1124
                                        self.backend.emit_add_r_imm(&x86_64::RAX, fix_part_size as i32);
qinsoon's avatar
qinsoon committed
1125
1126

                                        // mov result to tmp_actual_size
qinsoon's avatar
qinsoon committed
1127
                                        self.backend.emit_mov_r_r(&tmp_actual_size, &x86_64::RAX);
qinsoon's avatar
qinsoon committed
1128
1129
1130
                                    }
                                }

qinsoon's avatar
qinsoon committed
1131
                                (tmp_actual_size, tmp_var_len)
qinsoon's avatar
qinsoon committed
1132
1133
1134
                            }
                        };

qinsoon's avatar
qinsoon committed
1135
                        let tmp_allocator = self.emit_get_allocator(node, f_content, f_context, vm);
1136
                        let tmp_res = self.emit_alloc_sequence(tmp_allocator.clone(), actual_size, ty_align, node, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
1137
1138
1139
1140
1141
1142
1143
1144
1145

                        // ASM: call muentry_init_object(%allocator, %tmp_res, %encode)
                        let encode = self.make_value_int_const(mm::get_gc_type_encode(ty_info.gc_type.id), vm);
                        self.emit_runtime_entry(
                            &entrypoints::INIT_HYBRID,
                            vec![tmp_allocator.clone(), tmp_res.clone(), encode, length],
                            None,
                            Some(node), f_content, f_context, vm
                        );
qinsoon's avatar
qinsoon committed
1146
                    }
qinsoon's avatar
qinsoon committed
1147
1148
                    
                    Instruction_::Throw(op_index) => {
qinsoon's avatar
qinsoon committed
1149
1150
                        trace!("instsel on THROW");

qinsoon's avatar
qinsoon committed
1151
1152
1153
1154
1155
1156
1157
                        let ops = inst.ops.read().unwrap();
                        let ref exception_obj = ops[op_index];
                        
                        self.emit_runtime_entry(
                            &entrypoints::THROW_EXCEPTION, 
                            vec![exception_obj.clone_value()], 
                            None,
qinsoon's avatar
qinsoon committed
1158
                            Some(node), f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
1159
                    }
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173

                    Instruction_::PrintHex(index) => {
                        trace!("instsel on PRINTHEX");

                        let ops = inst.ops.read().unwrap();
                        let ref op = ops[index];

                        self.emit_runtime_entry(
                            &entrypoints::PRINT_HEX,
                            vec![op.clone_value()],
                            None,
                            Some(node), f_content, f_context, vm
                        );
                    }
1174
1175
1176
1177
1178
1179
    
                    _ => unimplemented!()
                } // main switch
            },
            
            TreeNode_::Value(ref p) => {
1180
        
1181
1182
1183
1184
            }
        }
    }
    
1185
1186
1187
1188
    fn make_temporary(&mut self, f_context: &mut FunctionContext, ty: P<MuType>, vm: &VM) -> P<Value> {
        f_context.make_temporary(vm.next_id(), ty).clone_value()
    }
    
1189
    fn make_memory_op_base_offset (&mut self, base: &P<Value>, offset: i32, ty: P<MuType>, vm: &VM) -> P<Value> {
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
        P(Value{
            hdr: MuEntityHeader::unnamed(vm.next_id()),
            ty: ty.clone(),
            v: Value_::Memory(MemoryLocation::Address{
                base: base.clone(),
                offset: Some(self.make_value_int_const(offset as u64, vm)),
                index: None,
                scale: None
            })
        })
    }
qinsoon's avatar
qinsoon committed
1201

qinsoon's avatar
qinsoon committed
1202
    fn make_memory_op_base_index(&mut self, base: &P<Value>, index: &P<Value>, scale: u8, ty: P<MuType>, vm: &VM) -> P<Value> {
qinsoon's avatar
qinsoon committed
1203
1204
1205
1206
1207
        P(Value{
            hdr: MuEntityHeader::unnamed(vm.next_id()),
            ty: ty.clone(),
            v: Value_::Memory(MemoryLocation::Address{
                base: base.clone(),