inst_sel.rs 283 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.

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

qinsoon's avatar
qinsoon committed
28
use compiler::backend::x86_64;
29 30
use compiler::backend::x86_64::callconv;
use compiler::backend::x86_64::callconv::CallConvResult;
31 32
use compiler::backend::x86_64::*;
use compiler::backend::*;
qinsoon's avatar
qinsoon committed
33
use compiler::frame::Frame;
34 35 36
use compiler::machine_code::CompiledFunction;
use compiler::CompilerPass;
use compiler::PROLOGUE_BLOCK_NAME;
37

38
use utils::math;
39
use utils::{BitSize, ByteSize};
40
use utils::{POINTER_SIZE, WORD_SIZE};
41

42
use std::any::Any;
43
use std::collections::HashMap;
44
use std::collections::LinkedList;
45
use std::sync::Arc;
46

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

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(
qinsoon's avatar
qinsoon committed
84 85
        MuType::new(new_internal_id(), MuType_::mustruct(Mu("quad_2"),
        vec![UINT64_TYPE.clone(); 2]))
86 87
    );

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

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

qinsoon's avatar
qinsoon committed
113
    /// constant for converting float to unsigned integer
qinsoon's avatar
qinsoon committed
114 115 116 117 118
    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))
    });
119 120
}

qinsoon's avatar
qinsoon committed
121 122 123 124
/// 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)
125
const INLINE_FASTPATH: bool = false;
126

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

qinsoon's avatar
qinsoon committed
132 133
    // information about the function being compiled
    /// ID of current function version being compiled
134
    current_fv_id: MuID,
135
    /// name of current function version being compiled
136
    current_fv_name: MuName,
137 138
    /// signature of current function being compiled
    current_sig: Option<P<MuFuncSig>>,
qinsoon's avatar
qinsoon committed
139
    /// used to create a unique callsite ID for current function
qinsoon's avatar
qinsoon committed
140
    current_callsite_id: usize,
qinsoon's avatar
qinsoon committed
141
    /// frame for current function
qinsoon's avatar
qinsoon committed
142
    current_frame: Option<Frame>,
qinsoon's avatar
qinsoon committed
143 144
    /// block that is currently being compiled
    /// the block may be created during instruction selection
qinsoon's avatar
qinsoon committed
145
    current_block: Option<MuName>,
qinsoon's avatar
qinsoon committed
146 147
    /// IR block that is currently being compiled
    /// use this block name to trace some information at IR level
148
    current_block_in_ir: Option<MuName>,
qinsoon's avatar
qinsoon committed
149
    /// start location of current function
qinsoon's avatar
qinsoon committed
150
    current_func_start: Option<ValueLocation>,
qinsoon's avatar
qinsoon committed
151 152 153 154
    /// 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 tuples is faster than a Map.
    /// A list of tuples, the first is the name of a callsite, the next is the callsite destination,
    /// the last is the size of arguments pushed on the stack
155
    current_callsites: LinkedList<(MuName, MuID, usize)>,
qinsoon's avatar
qinsoon committed
156
    // key: block id, val: block location
157
    current_exn_blocks: HashMap<MuID, MuName>,
qinsoon's avatar
qinsoon committed
158 159
    /// constants used in this function that are put to memory
    /// key: value id, val: constant value
160
    current_constants: HashMap<MuID, P<Value>>,
qinsoon's avatar
qinsoon committed
161 162
    /// constants used in this function that are put to memory
    /// key: value id, val: memory location
163
    current_constants_locs: HashMap<MuID, P<Value>>,
164 165
}

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

173
            current_fv_id: 0,
174
            current_fv_name: Arc::new(String::new()),
175
            current_sig: None,
qinsoon's avatar
qinsoon committed
176
            current_callsite_id: 0,
qinsoon's avatar
qinsoon committed
177
            current_frame: None,
qinsoon's avatar
qinsoon committed
178 179 180 181
            // which block we are generating code for
            current_block: None,
            // it is possible the block is newly created in instruction
            // selection
182
            // but sometimes we want to know its control flow
qinsoon's avatar
qinsoon committed
183
            // so we need to track what block it is from the IR
184

qinsoon's avatar
qinsoon committed
185 186 187
            // FIXME: ideally we should not create new blocks in instruction selection
            // see Issue #6
            current_block_in_ir: None,
qinsoon's avatar
qinsoon committed
188
            current_func_start: None,
189
            current_callsites: LinkedList::new(),
190 191 192
            current_exn_blocks: HashMap::new(),

            current_constants: HashMap::new(),
193
            current_constants_locs: HashMap::new(),
194 195
        }
    }
qinsoon's avatar
qinsoon committed
196 197 198 199 200

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

    /// 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.
204 205 206 207 208
    fn instruction_select(
        &mut self,
        node: &'a TreeNode,
        f_content: &FunctionContent,
        f_context: &mut FunctionContext,
209
        vm: &VM,
210
    ) {
211
        trace!("instsel on node#{} {}", node.id(), node);
212

qinsoon's avatar
qinsoon committed
213
        match node.v {
214 215
            TreeNode_::Instruction(ref inst) => {
                match inst.v {
216 217 218 219 220 221
                    Instruction_::Branch2 {
                        cond,
                        ref true_dest,
                        ref false_dest,
                        ..
                    } => {
qinsoon's avatar
qinsoon committed
222
                        trace!("instsel on BRANCH2");
223
                        let (fallthrough_dest, branch_dest) = (false_dest, true_dest);
224

225
                        let ref ops = inst.ops;
226 227
                        self.process_dest(&ops, fallthrough_dest, f_content, f_context, vm);
                        self.process_dest(&ops, branch_dest, f_content, f_context, vm);
228

229
                        let branch_target = f_content.get_block(branch_dest.target.id()).name();
230

231
                        let ref cond = ops[cond];
qinsoon's avatar
qinsoon committed
232
                        if self.match_cmp_res(cond) {
qinsoon's avatar
qinsoon committed
233
                            // this branch2's cond is from a comparison result
234
                            trace!("emit cmp_res-branch2");
235
                            match self.emit_cmp_res(cond, f_content, f_context, vm) {
qinsoon's avatar
qinsoon committed
236 237
                                op::CmpOp::EQ => self.backend.emit_je(branch_target),
                                op::CmpOp::NE => self.backend.emit_jne(branch_target),
238 239 240 241 242 243 244 245
                                op::CmpOp::UGE => self.backend.emit_jae(branch_target),
                                op::CmpOp::UGT => self.backend.emit_ja(branch_target),
                                op::CmpOp::ULE => self.backend.emit_jbe(branch_target),
                                op::CmpOp::ULT => self.backend.emit_jb(branch_target),
                                op::CmpOp::SGE => self.backend.emit_jge(branch_target),
                                op::CmpOp::SGT => self.backend.emit_jg(branch_target),
                                op::CmpOp::SLE => self.backend.emit_jle(branch_target),
                                op::CmpOp::SLT => self.backend.emit_jl(branch_target),
qinsoon's avatar
qinsoon committed
246 247 248

                                // floating point
                                op::CmpOp::FOEQ | op::CmpOp::FUEQ => {
qinsoon's avatar
qinsoon committed
249
                                    self.backend.emit_je(branch_target)
250
                                }
qinsoon's avatar
qinsoon committed
251
                                op::CmpOp::FONE | op::CmpOp::FUNE => {
qinsoon's avatar
qinsoon committed
252
                                    self.backend.emit_jne(branch_target)
253
                                }
qinsoon's avatar
qinsoon committed
254
                                op::CmpOp::FOGT | op::CmpOp::FUGT => {
qinsoon's avatar
qinsoon committed
255
                                    self.backend.emit_ja(branch_target)
256
                                }
qinsoon's avatar
qinsoon committed
257
                                op::CmpOp::FOGE | op::CmpOp::FUGE => {
qinsoon's avatar
qinsoon committed
258
                                    self.backend.emit_jae(branch_target)
259
                                }
qinsoon's avatar
qinsoon committed
260
                                op::CmpOp::FOLT | op::CmpOp::FULT => {
qinsoon's avatar
qinsoon committed
261
                                    self.backend.emit_jb(branch_target)
262
                                }
qinsoon's avatar
qinsoon committed
263
                                op::CmpOp::FOLE | op::CmpOp::FULE => {
qinsoon's avatar
qinsoon committed
264
                                    self.backend.emit_jbe(branch_target)
265
                                }
qinsoon's avatar
qinsoon committed
266

267
                                _ => unimplemented!(),
qinsoon's avatar
qinsoon committed
268 269
                            }
                        } else if self.match_ireg(cond) {
qinsoon's avatar
qinsoon committed
270 271
                            // this branch2 cond is a temporary with value, or an instruction that
                            // emits a temporary
qinsoon's avatar
qinsoon committed
272
                            trace!("emit ireg-branch2");
273

274
                            let cond_reg = self.emit_ireg(cond, f_content, f_context, vm);
275

qinsoon's avatar
qinsoon committed
276
                            // emit: cmp cond_reg 1
qinsoon's avatar
qinsoon committed
277
                            self.backend.emit_cmp_imm_r(1, &cond_reg);
qinsoon's avatar
qinsoon committed
278
                            // emit: je #branch_dest
279
                            self.backend.emit_je(branch_target);
qinsoon's avatar
qinsoon committed
280
                        } else {
qinsoon's avatar
qinsoon committed
281
                            panic!("unexpected cond in BRANCH2: {}", cond)
282
                        }
283
                    }
qinsoon's avatar
qinsoon committed
284

285 286 287
                    Instruction_::Select {
                        cond,
                        true_val,
288
                        false_val,
289
                    } => {
qinsoon's avatar
qinsoon committed
290 291
                        use ast::op::CmpOp::*;

qinsoon's avatar
qinsoon committed
292
                        trace!("instsel on SELECT");
293
                        let ref ops = inst.ops;
qinsoon's avatar
qinsoon committed
294 295 296 297 298

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

qinsoon's avatar
qinsoon committed
299
                        // generate comparison
300 301 302 303 304 305 306 307 308 309 310 311
                        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
312
                        // emit code to move values
qinsoon's avatar
qinsoon committed
313 314
                        if self.match_ireg(true_val) {
                            // moving integers/pointers
315
                            let tmp_res = self.get_result_value(node);
qinsoon's avatar
qinsoon committed
316

317
                            // use cmov for 16/32/64bit integer
qinsoon's avatar
qinsoon committed
318
                            // use jcc  for 8 bit
319
                            // FIXME: could use 32bit register to implement 8bit select
qinsoon's avatar
qinsoon committed
320 321 322
                            match tmp_res.ty.get_int_length() {
                                // cmov
                                Some(len) if len > 8 => {
323 324 325 326
                                    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);
qinsoon's avatar
qinsoon committed
327 328 329 330 331

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

                                    match cmpop {
332 333
                                        EQ => self.backend.emit_cmove_r_r(&tmp_res, &tmp_true),
                                        NE => self.backend.emit_cmovne_r_r(&tmp_res, &tmp_true),
qinsoon's avatar
qinsoon committed
334
                                        SGE => self.backend.emit_cmovge_r_r(&tmp_res, &tmp_true),
335
                                        SGT => self.backend.emit_cmovg_r_r(&tmp_res, &tmp_true),
qinsoon's avatar
qinsoon committed
336
                                        SLE => self.backend.emit_cmovle_r_r(&tmp_res, &tmp_true),
337
                                        SLT => self.backend.emit_cmovl_r_r(&tmp_res, &tmp_true),
qinsoon's avatar
qinsoon committed
338
                                        UGE => self.backend.emit_cmovae_r_r(&tmp_res, &tmp_true),
339
                                        UGT => self.backend.emit_cmova_r_r(&tmp_res, &tmp_true),
qinsoon's avatar
qinsoon committed
340
                                        ULE => self.backend.emit_cmovbe_r_r(&tmp_res, &tmp_true),
341
                                        ULT => self.backend.emit_cmovb_r_r(&tmp_res, &tmp_true),
qinsoon's avatar
qinsoon committed
342

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
                                        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
361

qinsoon's avatar
qinsoon committed
362
                                        // FFALSE/FTRUE unimplemented
363
                                        _ => unimplemented!(),
qinsoon's avatar
qinsoon committed
364 365
                                    }
                                }
qinsoon's avatar
qinsoon committed
366
                                // jcc - for 8-bits integer
qinsoon's avatar
qinsoon committed
367
                                _ => {
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
368 369 370
                                    let blk_true = make_block_name(&node.name(), "select_true");
                                    let blk_false = make_block_name(&node.name(), "select_false");
                                    let blk_end = make_block_name(&node.name(), "select_end");
qinsoon's avatar
qinsoon committed
371 372 373

                                    // jump to blk_true if true
                                    match cmpop {
374 375
                                        EQ => self.backend.emit_je(blk_true.clone()),
                                        NE => self.backend.emit_jne(blk_true.clone()),
qinsoon's avatar
qinsoon committed
376
                                        SGE => self.backend.emit_jge(blk_true.clone()),
377
                                        SGT => self.backend.emit_jg(blk_true.clone()),
qinsoon's avatar
qinsoon committed
378
                                        SLE => self.backend.emit_jle(blk_true.clone()),
379
                                        SLT => self.backend.emit_jl(blk_true.clone()),
qinsoon's avatar
qinsoon committed
380
                                        UGE => self.backend.emit_jae(blk_true.clone()),
381
                                        UGT => self.backend.emit_ja(blk_true.clone()),
qinsoon's avatar
qinsoon committed
382
                                        ULE => self.backend.emit_jbe(blk_true.clone()),
383
                                        ULT => self.backend.emit_jb(blk_true.clone()),
qinsoon's avatar
qinsoon committed
384

385
                                        FOEQ | FUEQ => self.backend.emit_je(blk_true.clone()),
qinsoon's avatar
qinsoon committed
386
                                        FONE | FUNE => self.backend.emit_jne(blk_true.clone()),
387
                                        FOGT | FUGT => self.backend.emit_ja(blk_true.clone()),
qinsoon's avatar
qinsoon committed
388
                                        FOGE | FUGE => self.backend.emit_jae(blk_true.clone()),
389
                                        FOLT | FULT => self.backend.emit_jb(blk_true.clone()),
qinsoon's avatar
qinsoon committed
390 391
                                        FOLE | FULE => self.backend.emit_jbe(blk_true.clone()),

qinsoon's avatar
qinsoon committed
392
                                        // FFALSE/FTRUE unimplemented
393
                                        _ => unimplemented!(),
qinsoon's avatar
qinsoon committed
394 395
                                    }

396
                                    // finishing current block
qinsoon's avatar
qinsoon committed
397
                                    self.finish_block();
398 399

                                    // blk_false:
qinsoon's avatar
qinsoon committed
400
                                    self.start_block(blk_false.clone());
qinsoon's avatar
qinsoon committed
401
                                    // mov false result here
402
                                    self.emit_move_node_to_value(
403
                                        &tmp_res, &false_val, f_content, f_context, vm,
404
                                    );
qinsoon's avatar
qinsoon committed
405 406 407
                                    // jmp to end
                                    self.backend.emit_jmp(blk_end.clone());
                                    // finishing current block
qinsoon's avatar
qinsoon committed
408
                                    self.finish_block();
qinsoon's avatar
qinsoon committed
409 410

                                    // blk_true:
qinsoon's avatar
qinsoon committed
411
                                    self.start_block(blk_true.clone());
qinsoon's avatar
qinsoon committed
412
                                    // mov true value -> result
413
                                    self.emit_move_node_to_value(
414
                                        &tmp_res, &true_val, f_content, f_context, vm,
415
                                    );
416
                                    self.finish_block();
qinsoon's avatar
qinsoon committed
417 418

                                    // blk_end:
419
                                    self.start_block(blk_end.clone());
qinsoon's avatar
qinsoon committed
420
                                }
qinsoon's avatar
qinsoon committed
421
                            }
422 423 424
                        } else if self.match_fpreg(true_val) {
                            let tmp_res = self.get_result_value(node);

Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
425 426 427
                            let blk_true = make_block_name(&node.name(), "select_true");
                            let blk_false = make_block_name(&node.name(), "select_false");
                            let blk_end = make_block_name(&node.name(), "select_end");
428 429 430

                            // jump to blk_true if true
                            match cmpop {
431 432
                                EQ => self.backend.emit_je(blk_true.clone()),
                                NE => self.backend.emit_jne(blk_true.clone()),
433
                                SGE => self.backend.emit_jge(blk_true.clone()),
434
                                SGT => self.backend.emit_jg(blk_true.clone()),
435
                                SLE => self.backend.emit_jle(blk_true.clone()),
436
                                SLT => self.backend.emit_jl(blk_true.clone()),
437
                                UGE => self.backend.emit_jae(blk_true.clone()),
438
                                UGT => self.backend.emit_ja(blk_true.clone()),
439
                                ULE => self.backend.emit_jbe(blk_true.clone()),
440
                                ULT => self.backend.emit_jb(blk_true.clone()),
441

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

449
                                _ => unimplemented!(),
450 451 452
                            }

                            // finishing current block
qinsoon's avatar
qinsoon committed
453
                            self.finish_block();
454 455

                            // blk_false:
qinsoon's avatar
qinsoon committed
456
                            self.start_block(blk_false.clone());
457
                            // mov false result here
458
                            self.emit_move_node_to_value(
459
                                &tmp_res, &false_val, f_content, f_context, vm,
460
                            );
461 462 463 464
                            // jmp to end
                            self.backend.emit_jmp(blk_end.clone());

                            // finishing current block
qinsoon's avatar
qinsoon committed
465
                            self.finish_block();
466 467

                            // blk_true:
qinsoon's avatar
qinsoon committed
468
                            self.start_block(blk_true.clone());
469
                            // mov true value -> result
470
                            self.emit_move_node_to_value(
471
                                &tmp_res, &true_val, f_content, f_context, vm,
472
                            );
qinsoon's avatar
qinsoon committed
473
                            self.finish_block();
474 475

                            // blk_end:
qinsoon's avatar
qinsoon committed
476
                            self.start_block(blk_end.clone());
qinsoon's avatar
qinsoon committed
477 478 479
                        } else {
                            unimplemented!()
                        }
480
                    }
qinsoon's avatar
qinsoon committed
481

qinsoon's avatar
qinsoon committed
482
                    Instruction_::CmpOp(_, _, _) => {
qinsoon's avatar
qinsoon committed
483
                        use ast::op::CmpOp::*;
qinsoon's avatar
qinsoon committed
484
                        trace!("instsel on CMPOP");
485

qinsoon's avatar
qinsoon committed
486
                        let tmp_res = self.get_result_value(node);
qinsoon's avatar
qinsoon committed
487 488
                        assert!(tmp_res.ty.get_int_length().is_some());
                        assert!(tmp_res.ty.get_int_length().unwrap() == 1);
qinsoon's avatar
qinsoon committed
489

490
                        // set byte to result
qinsoon's avatar
qinsoon committed
491
                        match self.emit_cmp_res(node, f_content, f_context, vm) {
492 493
                            EQ => self.backend.emit_sete_r(&tmp_res),
                            NE => self.backend.emit_setne_r(&tmp_res),
494
                            SGE => self.backend.emit_setge_r(&tmp_res),
495
                            SGT => self.backend.emit_setg_r(&tmp_res),
496
                            SLE => self.backend.emit_setle_r(&tmp_res),
497
                            SLT => self.backend.emit_setl_r(&tmp_res),
498
                            UGE => self.backend.emit_setae_r(&tmp_res),
499
                            UGT => self.backend.emit_seta_r(&tmp_res),
500
                            ULE => self.backend.emit_setbe_r(&tmp_res),
501
                            ULT => self.backend.emit_setb_r(&tmp_res),
502

503
                            FOEQ | FUEQ => self.backend.emit_sete_r(&tmp_res),
504
                            FONE | FUNE => self.backend.emit_setne_r(&tmp_res),
505
                            FOGT | FUGT => self.backend.emit_seta_r(&tmp_res),
506
                            FOGE | FUGE => self.backend.emit_setae_r(&tmp_res),
507
                            FOLT | FULT => self.backend.emit_setb_r(&tmp_res),
508
                            FOLE | FULE => self.backend.emit_setbe_r(&tmp_res),
qinsoon's avatar
qinsoon committed
509

qinsoon's avatar
qinsoon committed
510
                            // FFALSE/FTRUE
511
                            _ => unimplemented!(),
512 513 514
                        }
                    }

qinsoon's avatar
qinsoon committed
515
                    Instruction_::Branch1(ref dest) => {
qinsoon's avatar
qinsoon committed
516
                        trace!("instsel on BRANCH1");
517
                        let ref ops = inst.ops;
518

519
                        self.process_dest(&ops, dest, f_content, f_context, vm);
520

521
                        let target = f_content.get_block(dest.target.id()).name();
522
                        // jmp
qinsoon's avatar
qinsoon committed
523
                        self.backend.emit_jmp(target);
524
                    }
qinsoon's avatar
qinsoon committed
525

526 527 528
                    Instruction_::Switch {
                        cond,
                        ref default,
529
                        ref branches,
530
                    } => {
qinsoon's avatar
qinsoon committed
531
                        trace!("instsel on SWITCH");
532
                        let ref ops = inst.ops;
qinsoon's avatar
qinsoon committed
533 534 535 536 537
                        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
538 539 540 541
                            // 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
542 543 544 545 546 547 548
                            // 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);

549
                                let target = f_content.get_block(case_dest.target.id()).name();
qinsoon's avatar
qinsoon committed
550 551 552 553 554

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

                                    // cmp case cond
qinsoon's avatar
qinsoon committed
555
                                    self.backend.emit_cmp_imm_r(imm, &tmp_cond);
qinsoon's avatar
qinsoon committed
556 557 558
                                    // je dest
                                    self.backend.emit_je(target);
                                } else if self.match_ireg(case_op) {
559 560
                                    let tmp_case_op =
                                        self.emit_ireg(case_op, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
561 562

                                    // cmp case cond
qinsoon's avatar
qinsoon committed
563
                                    self.backend.emit_cmp_r_r(&tmp_case_op, &tmp_cond);
qinsoon's avatar
qinsoon committed
564 565 566
                                    // je dest
                                    self.backend.emit_je(target);
                                } else {
567 568 569 570
                                    panic!(
                                        "expecting ireg cond to be either iimm or ireg: {}",
                                        cond
                                    );
qinsoon's avatar
qinsoon committed
571
                                }
572 573

                                self.finish_block();
574
                                let block_name = make_block_name(
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
575
                                    &node.name(),
576
                                    format!("switch_not_met_case_{}", case_op_index).as_str(),
577
                                );
578
                                self.start_block(block_name);
qinsoon's avatar
qinsoon committed
579 580 581 582
                            }

                            // emit default
                            self.process_dest(&ops, default, f_content, f_context, vm);
583

584
                            let default_target = f_content.get_block(default.target.id()).name();
qinsoon's avatar
qinsoon committed
585 586
                            self.backend.emit_jmp(default_target);
                        } else {
qinsoon's avatar
qinsoon committed
587 588
                            // other EQ-comparable types, e.g. floating point
                            unimplemented!()
qinsoon's avatar
qinsoon committed
589 590
                        }
                    }
591 592

                    Instruction_::ExprCall { ref data, is_abort } => {
qinsoon's avatar
qinsoon committed
593 594
                        trace!("instsel on EXPRCALL");

qinsoon's avatar
qinsoon committed
595
                        if is_abort {
qinsoon's avatar
qinsoon committed
596 597 598
                            // if any exception throws from the callee,
                            // we should abort execution, otherwise rethrow the exception
                            // FIXME: implement is_abort
qinsoon's avatar
qinsoon committed
599
                            unimplemented!()
600
                        }
601

qinsoon's avatar
qinsoon committed
602 603 604 605
                        self.emit_mu_call(
                            inst, // inst: &Instruction,
                            data, // calldata: &CallData,
                            None, // resumption: Option<&ResumptionData>,
606
                            node, // cur_node: &TreeNode,
607
                            f_content, f_context, vm,
608 609 610 611 612
                        );
                    }

                    Instruction_::Call {
                        ref data,
613
                        ref resume,
614
                    } => {
qinsoon's avatar
qinsoon committed
615 616
                        trace!("instsel on CALL");

617 618 619 620
                        self.emit_mu_call(inst, data, Some(resume), node, f_content, f_context, vm);
                    }

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

qinsoon's avatar
qinsoon committed
623
                        if is_abort {
qinsoon's avatar
qinsoon committed
624 625 626
                            // if any exception throws from the callee,
                            // we should abort execution, otherwise rethrow the exception
                            // FIXME: implement is_abort
qinsoon's avatar
qinsoon committed
627 628 629
                            unimplemented!()
                        }

630
                        self.emit_c_call_ir(inst, data, None, node, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
631 632
                    }

633 634
                    Instruction_::CCall {
                        ref data,
635
                        ref resume,
636
                    } => {
qinsoon's avatar
qinsoon committed
637 638
                        trace!("instsel on CCALL");

qinsoon's avatar
qinsoon committed
639 640 641 642 643
                        self.emit_c_call_ir(
                            inst,
                            data,
                            Some(resume),
                            node,
644 645
                            f_content,
                            f_context,
646
                            vm,
647
                        );
qinsoon's avatar
qinsoon committed
648
                    }
649

650
                    Instruction_::Return(_) => {
qinsoon's avatar
qinsoon committed
651 652
                        trace!("instsel on RETURN");

653
                        self.emit_common_epilogue(inst, f_content, f_context, vm);
654

qinsoon's avatar
qinsoon committed
655
                        self.backend.emit_ret();
656 657
                    }

qinsoon's avatar
qinsoon committed
658
                    Instruction_::BinOp(op, op1, op2) => {
qinsoon's avatar
qinsoon committed
659 660
                        trace!("instsel on BINOP");

qinsoon's avatar
qinsoon committed
661
                        self.emit_binop(node, inst, op, op1, op2, f_content, f_context, vm);
662
                    }
qinsoon's avatar
qinsoon committed
663

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

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

qinsoon's avatar
qinsoon committed
669 670
                        let values = inst.value.as_ref().unwrap();
                        let mut status_value_index = 1;
qinsoon's avatar
qinsoon committed
671

672
                        // status flags only works with int operations
673
                        if RegGroup::get_from_value(&values[0]) == RegGroup::GPR {
qinsoon's avatar
qinsoon committed
674 675 676 677 678
                            // 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

679 680 681 682
                            // negative flag
                            if status.flag_n {
                                let tmp_status = values[status_value_index].clone();
                                status_value_index += 1;
qinsoon's avatar
qinsoon committed
683

684 685
                                self.backend.emit_sets_r8(&tmp_status);
                            }
qinsoon's avatar
qinsoon committed
686

687 688 689 690
                            // zero flag
                            if status.flag_z {
                                let tmp_status = values[status_value_index].clone();
                                status_value_index += 1;
qinsoon's avatar
qinsoon committed
691

692 693
                                self.backend.emit_setz_r8(&tmp_status);
                            }
qinsoon's avatar
qinsoon committed
694

695 696 697 698
                            // unsigned overflow
                            if status.flag_c {
                                let tmp_status = values[status_value_index].clone();
                                status_value_index += 1;
qinsoon's avatar
qinsoon committed
699

700 701 702 703
                                match op {
                                    BinOp::Add | BinOp::Sub | BinOp::Mul => {
                                        self.backend.emit_setb_r8(&tmp_status);
                                    }
704
                                    _ => panic!("Only Add/Sub/Mul has #C flag"),
qinsoon's avatar
qinsoon committed
705 706
                                }
                            }
qinsoon's avatar
qinsoon committed
707

708 709 710
                            // signed overflow
                            if status.flag_v {
                                let tmp_status = values[status_value_index].clone();
qinsoon's avatar
qinsoon committed
711

712 713 714 715
                                match op {
                                    BinOp::Add | BinOp::Sub | BinOp::Mul => {
                                        self.backend.emit_seto_r8(&tmp_status);
                                    }
716
                                    _ => panic!("Only Add/Sub/Mul has #V flag"),
qinsoon's avatar
qinsoon committed
717 718
                                }
                            }
719 720 721
                        } else if RegGroup::get_from_value(&values[0]) == RegGroup::GPREX {
                            unimplemented!()
                        } else {
qinsoon's avatar
[wip]  
qinsoon committed
722
                            panic!("only int operations allow binop status flags")
723 724
                        }
                    }
qinsoon's avatar
qinsoon committed
725

726 727 728 729
                    Instruction_::ConvOp {
                        operation,
                        ref from_ty,
                        ref to_ty,
730
                        operand,
731
                    } => {
qinsoon's avatar
qinsoon committed
732 733
                        trace!("instsel on CONVOP");

734
                        let ref ops = inst.ops;
qinsoon's avatar
qinsoon committed
735 736 737
                        let ref op = ops[operand];

                        match operation {
qinsoon's avatar
[wip]  
qinsoon committed
738
                            // Truncate (from int to int)
qinsoon's avatar
qinsoon committed
739
                            op::ConvOp::TRUNC => {
qinsoon's avatar
qinsoon committed
740
                                let tmp_res = self.get_result_value(node);
qinsoon's avatar
vm.rs  
qinsoon committed
741
                                let to_ty_size = vm.get_backend_type_size(tmp_res.ty.id());
qinsoon's avatar
qinsoon committed
742

qinsoon's avatar
qinsoon committed
743 744
                                if self.match_ireg(op) {
                                    let tmp_op = self.emit_ireg(op, f_content, f_context, vm);
745

qinsoon's avatar
qinsoon committed
746
                                    // mov op -> result
qinsoon's avatar
qinsoon committed
747
                                    match to_ty_size {
748 749 750 751 752 753 754 755 756 757 758 759 760
                                        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),
qinsoon's avatar
qinsoon committed
761 762
                                    }
                                } else if self.match_ireg_ex(op) {
qinsoon's avatar
qinsoon committed
763
                                    let (op_l, _) = self.emit_ireg_ex(op, f_content, f_context, vm);
qinsoon's avatar
qinsoon committed
764 765

                                    match to_ty_size {
766 767 768 769
                                        1 | 2 => self.backend.emit_movz_r_r(
                                            unsafe { &tmp_res.as_type(UINT32_TYPE.clone()) },
                                            &op_l,
                                        ),
qinsoon's avatar
qinsoon committed
770
                                        4 | 8 => self.backend.emit_mov_r_r(&tmp_res, &op_l),
771
                                        _ => panic!("unsupported int size: {}", to_ty_size),
772
                                    }
qinsoon's avatar
qinsoon committed
773 774 775 776
                                } else {
                                    panic!("unexpected op (expect ireg): {}", op);
                                }
                            }
qinsoon's avatar
[wip]  
qinsoon committed
777
                            // Zero extend (from int to int)
qinsoon's avatar
qinsoon committed
778
                            op::ConvOp::ZEXT => {
779 780 781
                                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
782

783
                                    // movz op -> result
qinsoon's avatar
vm.rs  
qinsoon committed
784
                                    let from_ty_size = vm.get_backend_type_size(from_ty.id());
785
                                    let to_ty_size = vm.get_backend_type_size(to_ty.id());
qinsoon's avatar
qinsoon committed
786

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

791
                                    if from_ty_size != to_ty_size {
qinsoon's avatar
qinsoon committed
792 793 794
                                        match (from_ty_size, to_ty_size) {
                                            // int32 to int64
                                            (4, 8) => {
qinsoon's avatar
qinsoon committed
795 796
                                                // zero extend from 32 bits to 64 bits is
                                                // a mov instruction
qinsoon's avatar
qinsoon committed
797 798 799
                                                // x86 does not have movzlq (32 to 64)

                                                // tmp_op is int32, but tmp_res is int64
qinsoon's avatar
qinsoon committed
800 801
                                                // we want to force a 32-to-32 mov, so high bits
                                                // of the destination will be zeroed
802 803
                                                let tmp_res32 =
                                                    unsafe { tmp_res.as_type(UINT32_TYPE.clone()) };
qinsoon's avatar
qinsoon committed
804 805 806 807 808

                                                self.backend.emit_mov_r_r(&tmp_res32, &tmp_op);
                                            }
                                            // any int to int128
                                            (_, 16) => {
809 810
                                                let (res_l, res_h) =
                                                    self.split_int128(&tmp_res, f_context, vm);
qinsoon's avatar
qinsoon committed
811

812 813 814 815 816 817 818
                                                // use the temp as 64bit temp, mask it
                                                let tmp_op64 =
                                                    unsafe { &tmp_op.as_type(UINT64_TYPE.clone()) };
                                                self.emit_apply_mask(
                                                    &tmp_op64,
                                                    from_ty_size * 8,
                                                    f_context,
819
                                                    vm,
820 821 822 823 824
                                                );

                                                // use temp as lower bits
                                                // clear higher bits
                                                self.backend.emit_mov_r_r(&res_l, &tmp_op64);
qinsoon's avatar
qinsoon committed
825 826
                                                self.backend.emit_mov_r_imm(&res_h, 0);
                                            }
qinsoon's avatar
[wip]  
qinsoon committed
827
                                            // other cases
qinsoon's avatar
qinsoon committed
828 829 830
                                            _ => {
                                                self.backend.emit_movz_r_r(&tmp_res, &tmp_op);
                                            }
qinsoon's avatar
qinsoon committed
831
                                        }
qinsoon's avatar
qinsoon committed
832
                                    } else {
833
                                        self.backend.emit_mov_r_r(&tmp_res, &tmp_op);
qinsoon's avatar
qinsoon committed
834
                                    }
835 836
                                } else {
                                    panic!("unexpected op (expect ireg): {}", op);
qinsoon's avatar
qinsoon committed
837
                                }
838
                            }
qinsoon's avatar
[wip]  
qinsoon committed
839
                            // Sign extend (from int to int)
qinsoon's avatar
qinsoon committed
840
                            op::ConvOp::SEXT => {
841 842 843
                                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
844

845
                                    // movs op -> result
qinsoon's avatar
vm.rs  
qinsoon committed
846
                                    let from_ty_size = vm.get_backend_type_size(from_ty.id());
847
                                    let to_ty_size = vm.get_backend_type_size(to_ty.id());
qinsoon's avatar
qinsoon committed