GitLab will be upgraded on 31 Jan 2023 from 2.00 pm (AEDT) to 3.00 pm (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_sel.rs 240 KB
Newer Older
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
1
// Copyright 2017 The Australian National University
2
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
3
4
5
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
6
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
9
10
11
12
13
14
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

15
use ast::ir::*;
16
use ast::ptr::*;
qinsoon's avatar
qinsoon committed
17
use ast::inst::*;
18
use ast::op;
qinsoon's avatar
qinsoon committed
19
use ast::op::*;
qinsoon's avatar
qinsoon committed
20
use ast::types::*;
qinsoon's avatar
qinsoon committed
21
use vm::VM;
qinsoon's avatar
qinsoon committed
22
use runtime::mm;
23
24
use runtime::mm::objectmodel::OBJECT_HEADER_SIZE;
use runtime::mm::objectmodel::OBJECT_HEADER_OFFSET;
25
26
27
28
use runtime::ValueLocation;
use runtime::thread;
use runtime::entrypoints;
use runtime::entrypoints::RuntimeEntrypoint;
29
30

use compiler::CompilerPass;
qinsoon's avatar
qinsoon committed
31
use compiler::backend::BackendType;
32
use compiler::backend::RegGroup;
qinsoon's avatar
qinsoon committed
33
use compiler::backend::PROLOGUE_BLOCK_NAME;
qinsoon's avatar
qinsoon committed
34
35
36
use compiler::backend::x86_64;
use compiler::backend::x86_64::CodeGenerator;
use compiler::backend::x86_64::ASMCodeGen;
qinsoon's avatar
qinsoon committed
37
38
use compiler::machine_code::CompiledFunction;
use compiler::frame::Frame;
39

40
use utils::math;
qinsoon's avatar
qinsoon committed
41
use utils::POINTER_SIZE;
42

43
use std::collections::HashMap;
44
45
use std::collections::LinkedList;

qinsoon's avatar
qinsoon committed
46
use std::any::Any;
47

48
lazy_static! {
qinsoon's avatar
qinsoon committed
49
50
    /// struct<int32, int32, int32, int32>
    static ref LONG_4_TYPE : P<MuType> = P(
51
52
53
        MuType::new(new_internal_id(), MuType_::mustruct(Mu("long_4"), vec![UINT32_TYPE.clone(); 4]))
    );

qinsoon's avatar
qinsoon committed
54
55
    /// constant for converting unsigned integer to floating point
    static ref UITOFP_C0 : P<Value> = P(Value{
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
81
        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))
            })
        ]))
    });

qinsoon's avatar
qinsoon committed
82
83
    /// struct<int64, int64>
    static ref QUAD_2_TYPE : P<MuType> = P(
84
85
86
        MuType::new(new_internal_id(), MuType_::mustruct(Mu("quad_2"), vec![UINT64_TYPE.clone(); 2]))
    );

qinsoon's avatar
qinsoon committed
87
88
    /// constant for converting unsigned integer to floating point
    static ref UITOFP_C1 : P<Value> = P(Value{
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
        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))
            })
        ]))
    });
104

qinsoon's avatar
qinsoon committed
105
106
    /// constant for converting double to unsigned integer
    static ref FPTOUI_C_DOUBLE : P<Value> = P(Value{
qinsoon's avatar
qinsoon committed
107
        hdr: MuEntityHeader::named(new_internal_id(), Mu("FPTOUI_C_DOUBLE")),
108
109
110
        ty : UINT64_TYPE.clone(),
        v  : Value_::Constant(Constant::Int(4890909195324358656u64))
    });
qinsoon's avatar
qinsoon committed
111

qinsoon's avatar
qinsoon committed
112
    /// constant for converting float to unsigned integer
qinsoon's avatar
qinsoon committed
113
114
115
116
117
    pub static ref FPTOUI_C_FLOAT : P<Value> = P(Value{
        hdr: MuEntityHeader::named(new_internal_id(), Mu("FPTOUI_C_FLOAT")),
        ty : UINT32_TYPE.clone(),
        v  : Value_::Constant(Constant::Int(1593835520u64))
    });
118
119
}

qinsoon's avatar
qinsoon committed
120
121
122
123
/// for some IR instructions, we need a call into runtime
/// for efficiency, we may emit runtime fastpath directly in assembly
//  FIXME: we should have a separate pass to rewrite the instruction into a fastpath (in IR),
//         and a call to slowpath, then instruction selection (see Issue#6)
124
125
const INLINE_FASTPATH : bool = false;

126
pub struct InstructionSelection {
127
    name: &'static str,
qinsoon's avatar
qinsoon committed
128
    /// backend code generator
129
    backend: Box<CodeGenerator>,
130

qinsoon's avatar
qinsoon committed
131
132
133
    // information about the function being compiled

    /// ID of current function version being compiled
134
    current_fv_id: MuID,
qinsoon's avatar
qinsoon committed
135
    /// used to create a unique callsite ID for current function
qinsoon's avatar
qinsoon committed
136
    current_callsite_id: usize,
qinsoon's avatar
qinsoon committed
137
    /// frame for current function
qinsoon's avatar
qinsoon committed
138
    current_frame: Option<Frame>,
qinsoon's avatar
qinsoon committed
139
140
    /// block that is currently being compiled
    /// the block may be created during instruction selection
qinsoon's avatar
qinsoon committed
141
    current_block: Option<MuName>,
qinsoon's avatar
qinsoon committed
142
143
    /// IR block that is currently being compiled
    /// use this block name to trace some information at IR level
144
    current_block_in_ir: Option<MuName>,
qinsoon's avatar
qinsoon committed
145
    /// start location of current function
qinsoon's avatar
qinsoon committed
146
    current_func_start: Option<ValueLocation>,
147
148
149
    /// technically this is a map in that each Key is unique, but we will never try and add duplicate
    /// keys, or look things up, so a list of pairs is faster than a Map.
    /// A list of pairs, the first is the name of a callsite the second
150
    current_callsites: LinkedList<(MuName, MuID)>,
qinsoon's avatar
qinsoon committed
151
152
    /// a map of exception blocks
    /// key: block id, val: block location
153
    current_exn_blocks: HashMap<MuID, MuName>,
qinsoon's avatar
qinsoon committed
154
155
    /// constants used in this function that are put to memory
    /// key: value id, val: constant value
156
    current_constants: HashMap<MuID, P<Value>>,
qinsoon's avatar
qinsoon committed
157
158
    /// constants used in this function that are put to memory
    /// key: value id, val: memory location
159
    current_constants_locs: HashMap<MuID, P<Value>>
160
161
}

162
impl <'a> InstructionSelection {
qinsoon's avatar
qinsoon committed
163
    #[cfg(feature = "aot")]
164
    pub fn new() -> InstructionSelection {
165
166
        InstructionSelection{
            name: "Instruction Selection (x64)",
167
            backend: Box::new(ASMCodeGen::new()),
168

169
            current_fv_id: 0,
qinsoon's avatar
qinsoon committed
170
            current_callsite_id: 0,
qinsoon's avatar
qinsoon committed
171
            current_frame: None,
172
173
174
175
176
177
178
            current_block: None,        // which block we are generating code for
            current_block_in_ir: None,  // it is possible the block is newly created in instruction selection
                                        // but sometimes we want to know its control flow
                                        // so we need to track what block it is from the IR

                                        // FIXME: ideally we should not create new blocks in instruction selection
                                        // see Issue #6
qinsoon's avatar
qinsoon committed
179
            current_func_start: None,
180
            current_callsites: LinkedList::new(),
181
182
183
184
            current_exn_blocks: HashMap::new(),

            current_constants: HashMap::new(),
            current_constants_locs: HashMap::new()
185
186
        }
    }
qinsoon's avatar
qinsoon committed
187
188
189
190
191

    #[cfg(feature = "jit")]
    pub fn new() -> InstructionSelection {
        unimplemented!()
    }
qinsoon's avatar
qinsoon committed
192
193
194

    /// we use hand-written pattern matching rules for instruction selection
    /// for a pattern that can match several rules, the first rule met will be executed, and chosen.
195
    fn instruction_select(&mut self, node: &'a TreeNode, f_content: &FunctionContent, f_context: &mut FunctionContext, vm: &VM) {
196
        trace!("instsel on node#{} {}", node.id(), node);
qinsoon's avatar
qinsoon committed
197
198
        
        match node.v {
199
200
            TreeNode_::Instruction(ref inst) => {
                match inst.v {
qinsoon's avatar
qinsoon committed
201
                    Instruction_::Branch2{cond, ref true_dest, ref false_dest, ..} => {
qinsoon's avatar
qinsoon committed
202
                        trace!("instsel on BRANCH2");
203
204
                        // '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)
qinsoon's avatar
qinsoon committed
205
206
                        // FIXME: we should move this to machine independent code
                        // e.g. as a subpass after trace scheduling, see Issue#27
207
                        let (fallthrough_dest, branch_dest, branch_if_true) = {
208
                            // get current block and next block in trace (fallthrough block)
209
                            let cur_block = f_content.get_block_by_name(self.current_block_in_ir.as_ref().unwrap().clone());
210
211
212
                            let next_block_in_trace = cur_block.control_flow.get_hottest_succ().unwrap();

                            if next_block_in_trace == true_dest.target {
213
214
215
                                (true_dest, false_dest, false)
                            } else {
                                (false_dest, true_dest, true)
216
                            }
217
                        };
218
                        
219
                        let ref ops = inst.ops;
220
221
                        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
222
                        
223
                        let branch_target = f_content.get_block(branch_dest.target).name().unwrap();
224
225
    
                        let ref cond = ops[cond];
qinsoon's avatar
qinsoon committed
226
                        if self.match_cmp_res(cond) {
qinsoon's avatar
qinsoon committed
227
                            // this branch2's cond is from a comparison result
228
                            trace!("emit cmp_res-branch2");
229
                            match self.emit_cmp_res(cond, f_content, f_context, vm) {
230
231
232
233
234
235
236
237
238
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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
                                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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345

                                // 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
346
347
348
                                _ => unimplemented!()
                            }
                        } else if self.match_ireg(cond) {
qinsoon's avatar
qinsoon committed
349
350
                            // this branch2 cond is a temporary with value, or an instruction that
                            // emits a temporary
qinsoon's avatar
qinsoon committed
351
                            trace!("emit ireg-branch2");
352
                            
353
                            let cond_reg = self.emit_ireg(cond, f_content, f_context, vm);
354
                            
qinsoon's avatar
qinsoon committed
355
                            // emit: cmp cond_reg 1
qinsoon's avatar
qinsoon committed
356
                            self.backend.emit_cmp_imm_r(1, &cond_reg);
qinsoon's avatar
qinsoon committed
357
                            // emit: je #branch_dest
358
359
360
361
362
                            if branch_if_true {
                                self.backend.emit_je(branch_target);
                            } else {
                                self.backend.emit_jne(branch_target);
                            }
qinsoon's avatar
qinsoon committed
363
                        } else {
qinsoon's avatar
qinsoon committed
364
                            panic!("unexpected cond in BRANCH2: {}", cond)
365
                        }
366
367

                        // it is possible that the fallthrough block is scheduled somewhere else
qinsoon's avatar
qinsoon committed
368
369
                        // we need to explicitly jump to it (this jump will get eliminated in
                        // peephole pass if the fallthrough block immediate follows the jump)
370
371
372
373
374
375
376
                        self.finish_block();

                        let fallthrough_temp_block = format!("{}_{}_branch_fallthrough", self.current_fv_id, node.id());
                        self.start_block(fallthrough_temp_block);

                        let fallthrough_target = f_content.get_block(fallthrough_dest.target).name().unwrap();
                        self.backend.emit_jmp(fallthrough_target);
377
                    },
qinsoon's avatar
qinsoon committed
378
379

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

qinsoon's avatar
qinsoon committed
382
                        trace!("instsel on SELECT");
383
                        let ref ops = inst.ops;
qinsoon's avatar
qinsoon committed
384
385
386
387
388

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

qinsoon's avatar
qinsoon committed
389
                        // generate comparison
390
391
392
393
394
395
396
397
398
399
400
401
                        let cmpop = if self.match_cmp_res(cond) {
                            self.emit_cmp_res(cond, f_content, f_context, vm)
                        } else if self.match_ireg(cond) {
                            let tmp_cond = self.emit_ireg(cond, f_content, f_context, vm);
                            // emit: cmp cond_reg 1
                            self.backend.emit_cmp_imm_r(1, &tmp_cond);

                            EQ
                        } else {
                            panic!("expected cond to be ireg, found {}", cond)
                        };

qinsoon's avatar
qinsoon committed
402
                        // emit code to move values
qinsoon's avatar
qinsoon committed
403
404
405
                        if self.match_ireg(true_val) {
                            // moving integers/pointers
                            let tmp_res   = self.get_result_value(node);
qinsoon's avatar
qinsoon committed
406

407
                            // use cmov for 16/32/64bit integer
qinsoon's avatar
qinsoon committed
408
                            // use jcc  for 8 bit
409
                            // FIXME: could use 32bit register to implement 8bit select
qinsoon's avatar
qinsoon committed
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
                            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),

qinsoon's avatar
qinsoon committed
438
                                        // FFALSE/FTRUE unimplemented
qinsoon's avatar
qinsoon committed
439
440
441
                                        _ => unimplemented!()
                                    }
                                }
qinsoon's avatar
qinsoon committed
442
                                // jcc - for 8-bits integer
qinsoon's avatar
qinsoon committed
443
                                _ => {
444
445
446
                                    let blk_true  = format!("{}_{}_select_true",  self.current_fv_id, node.id());
                                    let blk_false = format!("{}_{}_select_false", self.current_fv_id, node.id());
                                    let blk_end   = format!("{}_{}_select_end",   self.current_fv_id, node.id());
qinsoon's avatar
qinsoon committed
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467

                                    // 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()),

qinsoon's avatar
qinsoon committed
468
                                        // FFALSE/FTRUE unimplemented
qinsoon's avatar
qinsoon committed
469
470
471
                                        _ => unimplemented!()
                                    }

472
                                    // finishing current block
qinsoon's avatar
qinsoon committed
473
                                    self.finish_block();
474
475

                                    // blk_false:
qinsoon's avatar
qinsoon committed
476
                                    self.start_block(blk_false.clone());
qinsoon's avatar
qinsoon committed
477
478
479
480
481
                                    // 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
qinsoon's avatar
qinsoon committed
482
                                    self.finish_block();
qinsoon's avatar
qinsoon committed
483
484

                                    // blk_true:
qinsoon's avatar
qinsoon committed
485
                                    self.start_block(blk_true.clone());
qinsoon's avatar
qinsoon committed
486
487
                                    // mov true value -> result
                                    self.emit_move_node_to_value(&tmp_res, &true_val, f_content, f_context, vm);
488
                                    self.finish_block();
qinsoon's avatar
qinsoon committed
489
490

                                    // blk_end:
491
                                    self.start_block(blk_end.clone());
qinsoon's avatar
qinsoon committed
492
                                }
qinsoon's avatar
qinsoon committed
493
                            }
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
                        } else if self.match_fpreg(true_val) {
                            let tmp_res = self.get_result_value(node);

                            let blk_true  = format!("{}_{}_select_true",  self.current_fv_id, node.id());
                            let blk_false = format!("{}_{}_select_false", self.current_fv_id, node.id());
                            let blk_end   = format!("{}_{}_select_end",   self.current_fv_id, 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!()
                            }

                            // finishing current block
qinsoon's avatar
qinsoon committed
525
                            self.finish_block();
526
527

                            // blk_false:
qinsoon's avatar
qinsoon committed
528
                            self.start_block(blk_false.clone());
529
530
531
532
533
534
                            // 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
qinsoon's avatar
qinsoon committed
535
                            self.finish_block();
536
537

                            // blk_true:
qinsoon's avatar
qinsoon committed
538
                            self.start_block(blk_true.clone());
539
540
                            // mov true value -> result
                            self.emit_move_node_to_value(&tmp_res, &true_val, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
541
                            self.finish_block();
542
543

                            // blk_end:
qinsoon's avatar
qinsoon committed
544
                            self.start_block(blk_end.clone());
qinsoon's avatar
qinsoon committed
545
546
547
548
549
                        } else {
                            unimplemented!()
                        }
                    },

qinsoon's avatar
qinsoon committed
550
                    Instruction_::CmpOp(_, _, _) => {
qinsoon's avatar
qinsoon committed
551
                        use ast::op::CmpOp::*;
qinsoon's avatar
qinsoon committed
552
                        trace!("instsel on CMPOP");
553

qinsoon's avatar
qinsoon committed
554
                        let tmp_res = self.get_result_value(node);
qinsoon's avatar
qinsoon committed
555
556
                        assert!(tmp_res.ty.get_int_length().is_some());
                        assert!(tmp_res.ty.get_int_length().unwrap() == 1);
qinsoon's avatar
qinsoon committed
557

558
                        // set byte to result
qinsoon's avatar
qinsoon committed
559
                        match self.emit_cmp_res(node, f_content, f_context, vm) {
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
                            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
577

qinsoon's avatar
qinsoon committed
578
                            // FFALSE/FTRUE
qinsoon's avatar
qinsoon committed
579
                            _ => unimplemented!()
580
581
582
                        }
                    }

qinsoon's avatar
qinsoon committed
583
                    Instruction_::Branch1(ref dest) => {
qinsoon's avatar
qinsoon committed
584
                        trace!("instsel on BRANCH1");
585
                        let ref ops = inst.ops;
586
                                            
587
                        self.process_dest(&ops, dest, f_content, f_context, vm);
588
                        
589
                        let target = f_content.get_block(dest.target).name().unwrap();
590
                        // jmp
qinsoon's avatar
qinsoon committed
591
                        self.backend.emit_jmp(target);
592
                    },
qinsoon's avatar
qinsoon committed
593
594

                    Instruction_::Switch{cond, ref default, ref branches} => {
qinsoon's avatar
qinsoon committed
595
                        trace!("instsel on SWITCH");
596
                        let ref ops = inst.ops;
qinsoon's avatar
qinsoon committed
597
598
599
600
601
                        let ref cond = ops[cond];

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

qinsoon's avatar
qinsoon committed
602
603
604
605
                            // currently implementing switch as cascading conditional branch
                            // This is slow if there are many 'case' arms. We should consider
                            // using a switch table

qinsoon's avatar
qinsoon committed
606
607
608
609
610
611
612
613
614
615
616
617
618
                            // 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
619
                                    self.backend.emit_cmp_imm_r(imm, &tmp_cond);
qinsoon's avatar
qinsoon committed
620
621
622
623
624
625
                                    // 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
626
                                    self.backend.emit_cmp_r_r(&tmp_case_op, &tmp_cond);
qinsoon's avatar
qinsoon committed
627
628
629
630
631
                                    // je dest
                                    self.backend.emit_je(target);
                                } else {
                                    panic!("expecting ireg cond to be either iimm or ireg: {}", cond);
                                }
632
633
634

                                self.finish_block();
                                self.start_block(format!("{}_switch_not_met_case_{}", node.id(), case_op_index));
qinsoon's avatar
qinsoon committed
635
636
637
638
639
640
641
642
                            }

                            // 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 {
qinsoon's avatar
qinsoon committed
643
644
                            // other EQ-comparable types, e.g. floating point
                            unimplemented!()
qinsoon's avatar
qinsoon committed
645
646
                        }
                    }
647
                    
qinsoon's avatar
qinsoon committed
648
                    Instruction_::ExprCall{ref data, is_abort} => {
qinsoon's avatar
qinsoon committed
649
650
                        trace!("instsel on EXPRCALL");

qinsoon's avatar
qinsoon committed
651
                        if is_abort {
qinsoon's avatar
qinsoon committed
652
653
654
                            // if any exception throws from the callee,
                            // we should abort execution, otherwise rethrow the exception
                            // FIXME: implement is_abort
qinsoon's avatar
qinsoon committed
655
                            unimplemented!()
656
                        }
657
                        
qinsoon's avatar
qinsoon committed
658
659
660
661
662
                        self.emit_mu_call(
                            inst, // inst: &Instruction,
                            data, // calldata: &CallData,
                            None, // resumption: Option<&ResumptionData>,
                            node, // cur_node: &TreeNode, 
663
                            f_content, f_context, vm);
664
665
                    },
                    
qinsoon's avatar
qinsoon committed
666
                    Instruction_::Call{ref data, ref resume} => {
qinsoon's avatar
qinsoon committed
667
668
                        trace!("instsel on CALL");

qinsoon's avatar
qinsoon committed
669
670
671
672
673
674
                        self.emit_mu_call(
                            inst, 
                            data, 
                            Some(resume), 
                            node, 
                            f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
675
676
677
                    },

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

qinsoon's avatar
qinsoon committed
680
                        if is_abort {
qinsoon's avatar
qinsoon committed
681
682
683
                            // if any exception throws from the callee,
                            // we should abort execution, otherwise rethrow the exception
                            // FIXME: implement is_abort
qinsoon's avatar
qinsoon committed
684
685
686
                            unimplemented!()
                        }

qinsoon's avatar
qinsoon committed
687
688
689
690
691
692
                        self.emit_c_call_ir(
                            inst,
                            data,
                            None,
                            node,
                            f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
693
694
695
                    }

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

qinsoon's avatar
qinsoon committed
698
699
700
701
702
703
                        self.emit_c_call_ir(
                            inst,
                            data,
                            Some(resume),
                            node,
                            f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
704
705
                    }
                    
706
                    Instruction_::Return(_) => {
qinsoon's avatar
qinsoon committed
707
708
                        trace!("instsel on RETURN");

709
                        self.emit_common_epilogue(inst, f_content, f_context, vm);
710
                        
qinsoon's avatar
qinsoon committed
711
                        self.backend.emit_ret();
712
713
                    },
                    
qinsoon's avatar
qinsoon committed
714
                    Instruction_::BinOp(op, op1, op2) => {
qinsoon's avatar
qinsoon committed
715
716
                        trace!("instsel on BINOP");

qinsoon's avatar
qinsoon committed
717
718
                        self.emit_binop(node, inst, op, op1, op2, f_content, f_context, vm);
                    },
qinsoon's avatar
qinsoon committed
719

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

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

qinsoon's avatar
qinsoon committed
725
726
                        let values = inst.value.as_ref().unwrap();
                        let mut status_value_index = 1;
qinsoon's avatar
qinsoon committed
727

728
                        // status flags only works with int operations
729
                        if RegGroup::get_from_value(&values[0]) == RegGroup::GPR {
qinsoon's avatar
qinsoon committed
730
731
732
733
734
                            // for mul, div, idiv, some of the flags may not generated
                            // from the computation, and we may need extra code
                            // to get the flags
                            // FIXME: See Issue#22

735
736
737
738
                            // negative flag
                            if status.flag_n {
                                let tmp_status = values[status_value_index].clone();
                                status_value_index += 1;
qinsoon's avatar
qinsoon committed
739

740
741
                                self.backend.emit_sets_r8(&tmp_status);
                            }
qinsoon's avatar
qinsoon committed
742

743
744
745
746
                            // zero flag
                            if status.flag_z {
                                let tmp_status = values[status_value_index].clone();
                                status_value_index += 1;
qinsoon's avatar
qinsoon committed
747

748
749
                                self.backend.emit_setz_r8(&tmp_status);
                            }
qinsoon's avatar
qinsoon committed
750

751
752
753
754
                            // unsigned overflow
                            if status.flag_c {
                                let tmp_status = values[status_value_index].clone();
                                status_value_index += 1;
qinsoon's avatar
qinsoon committed
755

756
757
758
759
760
                                match op {
                                    BinOp::Add | BinOp::Sub | BinOp::Mul => {
                                        self.backend.emit_setb_r8(&tmp_status);
                                    }
                                    _ => panic!("Only Add/Sub/Mul has #C flag")
qinsoon's avatar
qinsoon committed
761
762
                                }
                            }
qinsoon's avatar
qinsoon committed
763

764
765
766
                            // signed overflow
                            if status.flag_v {
                                let tmp_status = values[status_value_index].clone();
qinsoon's avatar
qinsoon committed
767

768
769
770
771
772
                                match op {
                                    BinOp::Add | BinOp::Sub | BinOp::Mul => {
                                        self.backend.emit_seto_r8(&tmp_status);
                                    }
                                    _ => panic!("Only Add/Sub/Mul has #V flag")
qinsoon's avatar
qinsoon committed
773
774
                                }
                            }
775
776
777
                        } else if RegGroup::get_from_value(&values[0]) == RegGroup::GPREX {
                            unimplemented!()
                        } else {
qinsoon's avatar
[wip]    
qinsoon committed
778
                            panic!("only int operations allow binop status flags")
779
780
                        }
                    }
qinsoon's avatar
qinsoon committed
781
782

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

785
                        let ref ops = inst.ops;
qinsoon's avatar
qinsoon committed
786
787
788
                        let ref op = ops[operand];

                        match operation {
qinsoon's avatar
[wip]    
qinsoon committed
789
                            // Truncate (from int to int)
qinsoon's avatar
qinsoon committed
790
                            op::ConvOp::TRUNC => {
qinsoon's avatar
qinsoon committed
791
                                let tmp_res = self.get_result_value(node);
qinsoon's avatar
vm.rs    
qinsoon committed
792
                                let to_ty_size = vm.get_backend_type_size(tmp_res.ty.id());
qinsoon's avatar
qinsoon committed
793

qinsoon's avatar
qinsoon committed
794
795
                                if self.match_ireg(op) {
                                    let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
796

qinsoon's avatar
qinsoon committed
797
                                    // mov op -> result
qinsoon's avatar
qinsoon committed
798
799
800
801
802
803
804
805
                                    match to_ty_size {
                                        1 => self.backend.emit_mov_r_r(&tmp_res, unsafe {&tmp_op.as_type(UINT8_TYPE.clone())}),
                                        2 => self.backend.emit_mov_r_r(&tmp_res, unsafe {&tmp_op.as_type(UINT16_TYPE.clone())}),
                                        4 => self.backend.emit_mov_r_r(&tmp_res, unsafe {&tmp_op.as_type(UINT32_TYPE.clone())}),
                                        8 => self.backend.emit_mov_r_r(&tmp_res, unsafe {&tmp_op.as_type(UINT64_TYPE.clone())}),
                                        _  => panic!("unsupported int size: {}", to_ty_size)
                                    }
                                } else if self.match_ireg_ex(op) {
qinsoon's avatar
qinsoon committed
806
                                    let (op_l, _) = self.emit_ireg_ex(op, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
807
808
809
810
811

                                    match to_ty_size {
                                        1 | 2 => self.backend.emit_movz_r_r(unsafe {&tmp_res.as_type(UINT32_TYPE.clone())}, &op_l),
                                        4 | 8 => self.backend.emit_mov_r_r(&tmp_res, &op_l),
                                        _ => panic!("unsupported int size: {}", to_ty_size)
812
                                    }
qinsoon's avatar
qinsoon committed
813
814
815
816
                                } else {
                                    panic!("unexpected op (expect ireg): {}", op);
                                }
                            }
qinsoon's avatar
[wip]    
qinsoon committed
817
                            // Zero extend (from int to int)
qinsoon's avatar
qinsoon committed
818
                            op::ConvOp::ZEXT => {
819
820
821
                                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
822

823
                                    // movz op -> result
qinsoon's avatar
vm.rs    
qinsoon committed
824
825
                                    let from_ty_size = vm.get_backend_type_size(from_ty.id());
                                    let to_ty_size   = vm.get_backend_type_size(to_ty.id());
qinsoon's avatar
qinsoon committed
826

qinsoon's avatar
[wip]    
qinsoon committed
827
828
829
                                    // we treat int1 as int8, so it is possible from_ty_size == to_ty_size == 1 byte
                                    assert!(from_ty_size <= to_ty_size);

830
                                    if from_ty_size != to_ty_size {
qinsoon's avatar
qinsoon committed
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
                                        match (from_ty_size, to_ty_size) {
                                            // int32 to int64
                                            (4, 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);
                                            }
                                            // any int to int128
                                            (_, 16) => {
                                                let (res_l, res_h) = self.split_int128(&tmp_res, f_context, vm);

                                                self.backend.emit_mov_r_r(&res_l, unsafe {&tmp_op.as_type(UINT64_TYPE.clone())});
                                                self.backend.emit_mov_r_imm(&res_h, 0);
                                            }
qinsoon's avatar
[wip]    
qinsoon committed
850
                                            // other cases
qinsoon's avatar
qinsoon committed
851
852
853
                                            _ => {
                                                self.backend.emit_movz_r_r(&tmp_res, &tmp_op);
                                            }
qinsoon's avatar
qinsoon committed
854
                                        }
qinsoon's avatar
qinsoon committed
855
                                    } else {
856
                                        self.backend.emit_mov_r_r(&tmp_res, &tmp_op);
qinsoon's avatar
qinsoon committed
857
                                    }
858
859
                                } else {
                                    panic!("unexpected op (expect ireg): {}", op);
qinsoon's avatar
qinsoon committed
860
861
                                }
                            },
qinsoon's avatar
[wip]    
qinsoon committed
862
                            // Sign extend (from int to int)
qinsoon's avatar
qinsoon committed
863
                            op::ConvOp::SEXT => {
864
865
866
                                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
867

868
                                    // movs op -> result
qinsoon's avatar
vm.rs    
qinsoon committed
869
870
                                    let from_ty_size = vm.get_backend_type_size(from_ty.id());
                                    let to_ty_size   = vm.get_backend_type_size(to_ty.id());
qinsoon's avatar
qinsoon committed
871

qinsoon's avatar
[wip]    
qinsoon committed
872
873
874
                                    // we treat int1 as int8, so it is possible from_ty_size == to_ty_size == 1 byte
                                    assert!(from_ty_size <= to_ty_size);

875
                                    if from_ty_size != to_ty_size {
qinsoon's avatar
qinsoon committed
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
                                        match (from_ty_size, to_ty_size) {
                                            // int64 to int128
                                            (8, 16) => {
                                                let (res_l, res_h) = self.split_int128(&tmp_res, f_context, vm);

                                                // mov tmp_op -> res_h
                                                // sar res_h 63
                                                self.backend.emit_mov_r_r(&res_h, &tmp_op);
                                                self.backend.emit_sar_r_imm8(&res_h, 63i8);

                                                // mov tmp_op -> res_l
                                                self.backend.emit_mov_r_r(&res_l, &tmp_op);
                                            }
                                            // int32 to int128
                                            (_, 16) => {
                                                let (res_l, res_h) = self.split_int128(&tmp_res, f_context, vm);

                                                // movs tmp_op -> res_l
                                                self.backend.emit_movs_r_r(&res_l, &tmp_op);

                                                // mov res_l -> res_h
                                                // sar res_h 63
                                                self.backend.emit_mov_r_r(&res_h, unsafe {&res_l.as_type(UINT32_TYPE.clone())});
                                                self.backend.emit_sar_r_imm8(&res_h, 63i8);
                                            }
                                            _ => self.backend.emit_movs_r_r(&tmp_res, &tmp_op)
                                        }
qinsoon's avatar
qinsoon committed
903
                                    } else {
qinsoon's avatar
[wip]    
qinsoon committed
904
                                        // FIXME: sign extending <int1> 1 to <int8> is not a plain move
905
                                        self.backend.emit_mov_r_r(&tmp_res, &tmp_op);
qinsoon's avatar
qinsoon committed
906
                                    }
907
908
                                } else {
                                    panic!("unexpected op (expect ireg): {}", op)
qinsoon's avatar
qinsoon committed
909
910
                                }
                            }
qinsoon's avatar
[wip]    
qinsoon committed
911
                            // pointer/ref cast
qinsoon's avatar
qinsoon committed
912
913
914
915
916
917
                            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
918
                                    self.backend.emit_mov_r_r(&tmp_res, &tmp_op);
qinsoon's avatar
qinsoon committed
919
920
921
922
                                } else {
                                    panic!("unexpected op (expect ireg): {}", op)
                                }
                            }
qinsoon's avatar
[wip]    
qinsoon committed
923
                            // signed integer to floating point
qinsoon's avatar
qinsoon committed
924
925
926
                            op::ConvOp::SITOFP => {
                                let tmp_res = self.get_result_value(node);

927
928
929
930
931
932
933
934
935
936
937
                                if self.match_ireg(op) {
                                    let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
                                    match to_ty.v {
                                        MuType_::Double => self.backend.emit_cvtsi2sd_f64_r(&tmp_res, &tmp_op),
                                        MuType_::Float  => self.backend.emit_cvtsi2ss_f32_r(&tmp_res, &tmp_op),
                                        _ => panic!("expecting fp type as to type in SITOFP, found {}", to_ty)
                                    }
                                } else if self.match_ireg_ex(op) {
                                    unimplemented!()
                                } else {
                                    panic!("unexpected op (expect ireg/ireg_ex): {}", op)
qinsoon's avatar
qinsoon committed
938
939
                                }
                            }
qinsoon's avatar
[wip]    
qinsoon committed
940
                            // floating point to signed integer
qinsoon's avatar
qinsoon committed
941
942
943
                            op::ConvOp::FPTOSI => {
                                let tmp_res = self.get_result_value(node);

944
                                assert!(self.match_fpreg(op), "unexpected op (expected fpreg): {}", op);
qinsoon's avatar
[wip]    
qinsoon committed
945
                                let tmp_op = self.emit_fpreg(op, f_content, f_context, vm);
946

qinsoon's avatar
vm.rs    
qinsoon committed
947
                                let to_ty_size   = vm.get_backend_type_size(to_ty.id());
948
949
950
951
952
953
954
955
956
957
958
959
                                match to_ty_size {
                                    1 | 2 | 4 | 8 => {
                                        match from_ty.v {
                                            MuType_::Double => self.backend.emit_cvtsd2si_r_f64(&tmp_res, &tmp_op),
                                            MuType_::Float  => self.backend.emit_cvtss2si_r_f32(&tmp_res, &tmp_op),
                                            _ => panic!("expected fp type as from type in FPTOSI, found {}", from_ty)
                                        }
                                    }
                                    16 => {
                                        unimplemented!()
                                    }
                                    _ => panic!("unexpected support integer type as to_type: {}", to_ty)
qinsoon's avatar
qinsoon committed
960
                                }
961

qinsoon's avatar
qinsoon committed
962
                            }
qinsoon's avatar
[wip]    
qinsoon committed
963
                            // unsigned integer to floating point
964
965
966
                            op::ConvOp::UITOFP => {
                                let tmp_res = self.get_result_value(node);

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

qinsoon's avatar
vm.rs    
qinsoon committed
970
                                    let op_ty_size = vm.get_backend_type_size(tmp_op.ty.id());
971

972
973
974
975
976
                                    if to_ty.is_double() {
                                        match op_ty_size {
                                            8 => {
                                                // movd/movq op -> res
                                                self.backend.emit_mov_fpr_r64(&tmp_res, &tmp_op);
977

978
979
980
981
                                                // 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);
982

983
984
985
                                                // 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);
986

987
988
989
990
991
                                                // 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);
992

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

997
998
999
1000
1001
1002
1003
1004
                                                // 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)
1005
                                        }
1006
1007
1008
1009
1010
1011
                                    } else if to_ty.is_float() {
                                        match op_ty_size {
                                            8 => {
                                                // movl %tmp_op -> %tmp1
                                                let tmp1 = self.make_temporary(f_context, UINT32_TYPE.clone(), vm);
                                                self.backend.emit_mov_r_r(&tmp1, unsafe {&tmp_op.as_type(UINT32_TYPE.clone())});
qinsoon's avatar
qinsoon committed
1012

1013
1014
                                                // andl %tmp1 $1 -> %tmp1
                                                self.backend.emit_and_r_imm(&tmp1, 1);
qinsoon's avatar
qinsoon committed
1015

1016
1017
                                                // testq %tmp_op %tmp_op
                                                self.backend.emit_test_r_r(&tmp_op, &tmp_op);
qinsoon's avatar
qinsoon committed
1018

1019
1020
1021
                                                let blk_if_signed     = format!("{}_{}_uitofp_float_if_signed", self.current_fv_id, node.id());
                                                let blk_if_not_signed = format!("{}_{}_uitofp_float_if_not_signed", self.current_fv_id, node.id());
                                                let blk_done          = format!("{}_{}_uitofp_float_done", self.current_fv_id, node.id());
qinsoon's avatar
qinsoon committed
1022

1023
1024
1025
                                                // js %if_signed
                                                self.backend.emit_js(blk_if_signed.clone());
                                                self.finish_block();
qinsoon's avatar
qinsoon committed
1026

1027
1028
                                                // blk_if_not_signed:
                                                self.start_block(blk_if_not_signed);
qinsoon's avatar
qinsoon committed
1029

1030
1031
                                                // cvtsi2ss %tmp_op -> %tmp_res
                                                self.backend.emit_cvtsi2ss_f32_r(&tmp_res, &tmp_op);
qinsoon's avatar
qinsoon committed
1032

1033
1034
1035
                                                // jmp blk_done
                                                self.backend.emit_jmp(blk_done.clone());
                                                self.finish_block();
qinsoon's avatar
qinsoon committed
1036

1037
1038
                                                // blk_if_signed:
                                                self.start_block(blk_if_signed);
qinsoon's avatar
qinsoon committed
1039

1040
1041
                                                // shr %tmp_op $1 -> %tmp_op
                                                self.backend.emit_shr_r_imm8(&tmp_op, 1);
qinsoon's avatar
qinsoon committed
1042

1043
1044
                                                // or %tmp_op %tmp1 -> %tmp1
                                                self.backend.emit_or_r_r(unsafe {&tmp1.as_type(UINT64_TYPE.clone())}, &tmp_op);
qinsoon's avatar
qinsoon committed
1045

1046
1047
                                                // cvtsi2ss %tmp1 -> %tmp_res
                                                self.backend.emit_cvtsi2ss_f32_r(&tmp_res, &tmp1);