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.

asm_backend.rs 86.4 KB
Newer Older
1
2
#![allow(unused_variables)]

3
use compiler::backend::AOT_EMIT_CONTEXT_FILE;
qinsoon's avatar
qinsoon committed
4
use compiler::backend::RegGroup;
qinsoon's avatar
qinsoon committed
5
use utils::ByteSize;
6
use compiler::backend::x86_64;
7
use compiler::backend::x86_64::CodeGenerator;
qinsoon's avatar
qinsoon committed
8
use compiler::backend::{Reg, Mem};
qinsoon's avatar
qinsoon committed
9
use compiler::backend::x86_64::check_op_len;
qinsoon's avatar
qinsoon committed
10
use compiler::machine_code::MachineCode;
qinsoon's avatar
qinsoon committed
11
use vm::VM;
qinsoon's avatar
qinsoon committed
12
use runtime::ValueLocation;
13

qinsoon's avatar
qinsoon committed
14
use utils::vec_utils;
15
use utils::string_utils;
16
17
18
use ast::ptr::P;
use ast::ir::*;

qinsoon's avatar
qinsoon committed
19
20
use std::collections::HashMap;
use std::str;
21
use std::usize;
22
use std::slice::Iter;
23
use std::ops;
qinsoon's avatar
qinsoon committed
24
25

struct ASMCode {
qinsoon's avatar
qinsoon committed
26
    name: MuName, 
qinsoon's avatar
qinsoon committed
27
    code: Vec<ASMInst>,
qinsoon's avatar
qinsoon committed
28

qinsoon's avatar
qinsoon committed
29
30
31
    blocks: HashMap<MuName, ASMBlock>,

    frame_size_patchpoints: Vec<ASMLocation>
qinsoon's avatar
qinsoon committed
32
33
}

qinsoon's avatar
qinsoon committed
34
35
36
unsafe impl Send for ASMCode {} 
unsafe impl Sync for ASMCode {}

37
impl ASMCode {
qinsoon's avatar
qinsoon committed
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    fn get_use_locations(&self, reg: MuID) -> Vec<ASMLocation> {
        let mut ret = vec![];

        for inst in self.code.iter() {
            match inst.uses.get(&reg) {
                Some(ref locs) => {
                    ret.append(&mut locs.to_vec());
                },
                None => {}
            }
        }

        ret
    }

    fn get_define_locations(&self, reg: MuID) -> Vec<ASMLocation> {
        let mut ret = vec![];

        for inst in self.code.iter() {
            match inst.defines.get(&reg) {
                Some(ref locs) => {
                    ret.append(&mut locs.to_vec());
                },
                None => {}
            }
        }

        ret
    }

qinsoon's avatar
qinsoon committed
68
69
70
71
72
73
74
75
76
    fn is_block_start(&self, inst: usize) -> bool {
        for block in self.blocks.values() {
            if block.start_inst == inst {
                return true;
            }
        }
        false
    }

77
    fn is_last_inst_in_block(&self, inst: usize) -> bool {
qinsoon's avatar
qinsoon committed
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
        for block in self.blocks.values() {
            if block.end_inst == inst + 1 {
                return true;
            }
        }
        false
    }

    fn get_block_by_inst(&self, inst: usize) -> (&String, &ASMBlock) {
        for (name, block) in self.blocks.iter() {
            if inst >= block.start_inst && inst < block.end_inst {
                return (name, block);
            }
        }

        panic!("didnt find any block for inst {}", inst)
    }

qinsoon's avatar
qinsoon committed
96
97
98
99
100
101
102
103
104
105
    fn get_block_by_start_inst(&self, inst: usize) -> Option<&ASMBlock> {
        for block in self.blocks.values() {
            if block.start_inst == inst {
                return Some(block);
            }
        }

        None
    }

106
107
108
109
110
    fn rewrite_insert(
        &self,
        insert_before: HashMap<usize, Vec<Box<ASMCode>>>,
        insert_after: HashMap<usize, Vec<Box<ASMCode>>>) -> Box<ASMCode>
    {
111
        trace!("insert spilling code");
112
113
114
        let mut ret = ASMCode {
            name: self.name.clone(),
            code: vec![],
qinsoon's avatar
qinsoon committed
115
            blocks: hashmap!{},
qinsoon's avatar
qinsoon committed
116
            frame_size_patchpoints: vec![]
117
118
119
120
        };

        // iterate through old machine code
        let mut inst_offset = 0;    // how many instructions has been inserted
qinsoon's avatar
qinsoon committed
121
        let mut cur_block_start = usize::MAX;
122

qinsoon's avatar
qinsoon committed
123
124
125
126
        // inst N in old machine code is N' in new machine code
        // this map stores the relationship
        let mut location_map : HashMap<usize, usize> = HashMap::new();

127
        for i in 0..self.number_of_insts() {
128
129
            trace!("Inst{}", i);

qinsoon's avatar
qinsoon committed
130
131
            if self.is_block_start(i) {
                cur_block_start = i + inst_offset;
132
                trace!("  block start is shifted to {}", cur_block_start);
qinsoon's avatar
qinsoon committed
133
134
            }

135
136
137
138
139
            // insert code before this instruction
            if insert_before.contains_key(&i) {
                for insert in insert_before.get(&i).unwrap() {
                    ret.append_code_sequence_all(insert);
                    inst_offset += insert.number_of_insts();
140
                    trace!("  inserted {} insts before", insert.number_of_insts());
141
142
143
144
                }
            }

            // copy this instruction
qinsoon's avatar
qinsoon committed
145
146
            let mut inst = self.code[i].clone();

qinsoon's avatar
qinsoon committed
147
148
            // old ith inst is now the (i + inst_offset)th instruction
            location_map.insert(i, i + inst_offset);
149
            trace!("  Inst{} is now Inst{}", i, i + inst_offset);
qinsoon's avatar
qinsoon committed
150

qinsoon's avatar
qinsoon committed
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
            // this instruction has been offset by several instructions('inst_offset')
            // update its info
            // 1. fix defines and uses
            for locs in inst.defines.values_mut() {
                for loc in locs {
                    debug_assert!(loc.line == i);
                    loc.line += inst_offset;
                }
            }
            for locs in inst.uses.values_mut() {
                for loc in locs {
                    debug_assert!(loc.line == i);
                    loc.line += inst_offset;
                }
            }
qinsoon's avatar
qinsoon committed
166
167
168
            // 2. we need to delete existing preds/succs - CFA is required later
            inst.preds.clear();
            inst.succs.clear();
qinsoon's avatar
qinsoon committed
169
170
            // 3. add the inst
            ret.code.push(inst);
171
172
173
174


            // insert code after this instruction
            if insert_after.contains_key(&i) {
qinsoon's avatar
qinsoon committed
175
176
177
                for insert in insert_after.get(&i).unwrap() {
                    ret.append_code_sequence_all(insert);
                    inst_offset += insert.number_of_insts();
178
                    trace!("  inserted {} insts after", insert.number_of_insts());
qinsoon's avatar
qinsoon committed
179
180
181
                }
            }

182
183
            if self.is_last_inst_in_block(i) {
                let cur_block_end = i + 1 + inst_offset;
184

qinsoon's avatar
qinsoon committed
185
186
187
                // copy the block
                let (name, block) = self.get_block_by_inst(i);

qinsoon's avatar
qinsoon committed
188
                let new_block = ASMBlock{
189
190
191
192
193
194
195
196
197
198
                    start_inst: cur_block_start,
                    end_inst: cur_block_end,

                    livein: vec![],
                    liveout: vec![]
                };

                trace!("  old block: {:?}", block);
                trace!("  new block: {:?}", new_block);

qinsoon's avatar
qinsoon committed
199
200
201
202
                cur_block_start = usize::MAX;

                // add to the new code
                ret.blocks.insert(name.clone(), new_block);
203
204
205
            }
        }

qinsoon's avatar
qinsoon committed
206
207
208
209
210
        // fix patchpoint
        for patchpoint in self.frame_size_patchpoints.iter() {
            let new_patchpoint = ASMLocation {
                line: *location_map.get(&patchpoint.line).unwrap(),
                index: patchpoint.index,
qinsoon's avatar
qinsoon committed
211
212
                len: patchpoint.len,
                oplen: patchpoint.oplen
qinsoon's avatar
qinsoon committed
213
214
215
216
217
            };

            ret.frame_size_patchpoints.push(new_patchpoint);
        }

qinsoon's avatar
qinsoon committed
218
219
220
        ret.control_flow_analysis();

        Box::new(ret)
221
222
223
224
225
226
227
228
    }

    fn append_code_sequence(
        &mut self,
        another: &Box<ASMCode>,
        start_inst: usize,
        n_insts: usize)
    {
qinsoon's avatar
qinsoon committed
229
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
        let base_line = self.number_of_insts();

        for i in 0..n_insts {
            let cur_line_in_self = base_line + i;
            let cur_line_from_copy = start_inst + i;

            let mut inst = another.code[cur_line_from_copy].clone();

            // fix info
            for locs in inst.defines.values_mut() {
                for loc in locs {
                    debug_assert!(loc.line == i);
                    loc.line = cur_line_in_self;
                }
            }
            for locs in inst.uses.values_mut() {
                for loc in locs {
                    debug_assert!(loc.line == i);
                    loc.line = cur_line_in_self;
                }
            }
            // ignore preds/succs

            // add to self
            self.code.push(inst);
        }
255
256
257
258
259
260
    }

    fn append_code_sequence_all(&mut self, another: &Box<ASMCode>) {
        let n_insts = another.number_of_insts();
        self.append_code_sequence(another, 0, n_insts)
    }
qinsoon's avatar
qinsoon committed
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
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362

    fn control_flow_analysis(&mut self) {
        const TRACE_CFA : bool = false;

        // control flow analysis
        let n_insts = self.number_of_insts();

        let ref blocks = self.blocks;
        let ref mut asm = self.code;

        let block_start = {
            let mut ret = vec![];
            for block in blocks.values() {
                ret.push(block.start_inst);
            }
            ret
        };

        for i in 0..n_insts {
            // determine predecessor - if cur is not block start, its predecessor is previous insts
            let is_block_start = block_start.contains(&i);
            if !is_block_start {
                if i > 0 {
                    if TRACE_CFA {
                        trace!("inst {}: not a block start", i);
                        trace!("inst {}: set PREDS as previous inst {}", i, i - 1);
                    }
                    asm[i].preds.push(i - 1);
                }
            } else {
                // if cur is a branch target, we already set its predecessor
                // if cur is a fall-through block, we set it in a sanity check pass
            }

            // determine successor
            let branch = asm[i].branch.clone();
            match branch {
                ASMBranchTarget::Unconditional(ref target) => {
                    // branch to target
                    let target_n = self.blocks.get(target).unwrap().start_inst;

                    // cur inst's succ is target
                    asm[i].succs.push(target_n);

                    // target's pred is cur
                    asm[target_n].preds.push(i);

                    if TRACE_CFA {
                        trace!("inst {}: is a branch to {}", i, target);
                        trace!("inst {}: branch target index is {}", i, target_n);
                        trace!("inst {}: set SUCCS as branch target {}", i, target_n);
                        trace!("inst {}: set PREDS as branch source {}", target_n, i);
                    }
                },
                ASMBranchTarget::Conditional(ref target) => {
                    // branch to target
                    let target_n = self.blocks.get(target).unwrap().start_inst;

                    // cur insts' succ is target and next inst
                    asm[i].succs.push(target_n);

                    if TRACE_CFA {
                        trace!("inst {}: is a cond branch to {}", i, target);
                        trace!("inst {}: branch target index is {}", i, target_n);
                        trace!("inst {}: set SUCCS as branch target {}", i, target_n);
                    }

                    if i < n_insts - 1 {
                        if TRACE_CFA {
                            trace!("inst {}: set SUCCS as next inst", i + 1);
                        }
                        asm[i].succs.push(i + 1);
                    }

                    // target's pred is cur
                    asm[target_n].preds.push(i);
                    if TRACE_CFA {
                        trace!("inst {}: set PREDS as {}", target_n, i);
                    }
                },
                ASMBranchTarget::None => {
                    // not branch nor cond branch, succ is next inst
                    if TRACE_CFA {
                        trace!("inst {}: not a branch inst", i);
                    }
                    if i < n_insts - 1 {
                        if TRACE_CFA {
                            trace!("inst {}: set SUCCS as next inst {}", i, i + 1);
                        }
                        asm[i].succs.push(i + 1);
                    }
                }
            }
        }

        // a sanity check for fallthrough blocks
        for i in 0..n_insts {
            if i != 0 && asm[i].preds.len() == 0 {
                asm[i].preds.push(i - 1);
            }
        }
    }
qinsoon's avatar
qinsoon committed
363
364
365
366

    fn add_frame_size_patchpoint(&mut self, patchpoint: ASMLocation) {
        self.frame_size_patchpoints.push(patchpoint);
    }
367
368
369
370
}

use std::any::Any;

371
impl MachineCode for ASMCode {
372
373
374
    fn as_any(&self) -> &Any {
        self
    }
375
376
377
    fn number_of_insts(&self) -> usize {
        self.code.len()
    }
qinsoon's avatar
qinsoon committed
378
    
379
380
381
    fn is_move(&self, index: usize) -> bool {
        let inst = self.code.get(index);
        match inst {
qinsoon's avatar
qinsoon committed
382
383
            Some(inst) => inst.code.starts_with("mov")
                && !(inst.code.starts_with("movs") || inst.code.starts_with("movz")),
384
385
386
387
            None => false
        }
    }
    
qinsoon's avatar
qinsoon committed
388
    fn is_using_mem_op(&self, index: usize) -> bool {
qinsoon's avatar
qinsoon committed
389
        self.code[index].is_mem_op_used
qinsoon's avatar
qinsoon committed
390
391
    }
    
qinsoon's avatar
qinsoon committed
392
    fn get_succs(&self, index: usize) -> &Vec<usize> {
qinsoon's avatar
qinsoon committed
393
        &self.code[index].succs
qinsoon's avatar
qinsoon committed
394
395
396
    }
    
    fn get_preds(&self, index: usize) -> &Vec<usize> {
qinsoon's avatar
qinsoon committed
397
        &self.code[index].preds
qinsoon's avatar
qinsoon committed
398
399
    }
    
qinsoon's avatar
qinsoon committed
400
401
    fn get_inst_reg_uses(&self, index: usize) -> Vec<MuID> {
        self.code[index].uses.keys().map(|x| *x).collect()
402
403
    }
    
qinsoon's avatar
qinsoon committed
404
405
    fn get_inst_reg_defines(&self, index: usize) -> Vec<MuID> {
        self.code[index].defines.keys().map(|x| *x).collect()
406
    }
407
    
408
    fn replace_reg(&mut self, from: MuID, to: MuID) {
qinsoon's avatar
qinsoon committed
409
410
        for loc in self.get_define_locations(from) {
            let ref mut inst_to_patch = self.code[loc.line];
qinsoon's avatar
qinsoon committed
411
412
413
414
415
416
417

            // pick the right reg based on length
            let to_reg = x86_64::get_alias_for_length(to, loc.oplen);
            let to_reg_tag = to_reg.name().unwrap();
            let to_reg_string = "%".to_string() + &to_reg_tag;

            string_utils::replace(&mut inst_to_patch.code, loc.index, &to_reg_string, to_reg_string.len());
418
        }
419

qinsoon's avatar
qinsoon committed
420
421
        for loc in self.get_use_locations(from) {
            let ref mut inst_to_patch = self.code[loc.line];
qinsoon's avatar
qinsoon committed
422
423
424
425
426
427
428

            // pick the right reg based on length
            let to_reg = x86_64::get_alias_for_length(to, loc.oplen);
            let to_reg_tag = to_reg.name().unwrap();
            let to_reg_string = "%".to_string() + &to_reg_tag;

            string_utils::replace(&mut inst_to_patch.code, loc.index, &to_reg_string, to_reg_string.len());
429
430
        }
    }
431

432
    fn replace_define_tmp_for_inst(&mut self, from: MuID, to: MuID, inst: usize) {
qinsoon's avatar
qinsoon committed
433
        let to_reg_string : MuName = REG_PLACEHOLDER.clone();
434

qinsoon's avatar
qinsoon committed
435
436
437
438
439
440
        let asm = &mut self.code[inst];
        // if this reg is defined, replace the define
        if asm.defines.contains_key(&from) {
            let define_locs = asm.defines.get(&from).unwrap().to_vec();
            // replace temps
            for loc in define_locs.iter() {
qinsoon's avatar
qinsoon committed
441
                string_utils::replace(&mut asm.code, loc.index, &to_reg_string, to_reg_string.len());
442
443
            }

qinsoon's avatar
qinsoon committed
444
445
            // remove old key, insert new one
            asm.defines.remove(&from);
446
            asm.defines.insert(to, define_locs);
447
        }
448
449
450
    }

    fn replace_use_tmp_for_inst(&mut self, from: MuID, to: MuID, inst: usize) {
qinsoon's avatar
qinsoon committed
451
        let to_reg_string : MuName = REG_PLACEHOLDER.clone();
452
453

        let asm = &mut self.code[inst];
qinsoon's avatar
qinsoon committed
454
455
456
457
458
459

        // if this reg is used, replace the use
        if asm.uses.contains_key(&from) {
            let use_locs = asm.uses.get(&from).unwrap().to_vec();
            // replace temps
            for loc in use_locs.iter() {
qinsoon's avatar
qinsoon committed
460
                string_utils::replace(&mut asm.code, loc.index, &to_reg_string, to_reg_string.len());
461
462
            }

qinsoon's avatar
qinsoon committed
463
464
            // remove old key, insert new one
            asm.uses.remove(&from);
465
            asm.uses.insert(to, use_locs);
466
467
        }
    }
468
    
469
470
    fn set_inst_nop(&mut self, index: usize) {
        self.code.remove(index);
qinsoon's avatar
qinsoon committed
471
        self.code.insert(index, ASMInst::nop());
472
    }
qinsoon's avatar
qinsoon committed
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
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

    fn remove_unnecessary_callee_saved(&mut self, used_callee_saved: Vec<MuID>) -> Vec<MuID> {
        // we always save rbp
        let rbp = x86_64::RBP.extract_ssa_id().unwrap();
        // every push/pop will use/define rsp
        let rsp = x86_64::RSP.extract_ssa_id().unwrap();

        let find_op_other_than_rsp = |inst: &ASMInst| -> Option<MuID> {
            for id in inst.defines.keys() {
                if *id != rsp && *id != rbp {
                    return Some(*id);
                }
            }
            for id in inst.uses.keys() {
                if *id != rsp && *id != rbp {
                    return Some(*id);
                }
            }

            None
        };

        let mut inst_to_remove = vec![];
        let mut regs_to_remove = vec![];

        for i in 0..self.number_of_insts() {
            let ref inst = self.code[i];

            if inst.code.contains("push") || inst.code.contains("pop") {
                match find_op_other_than_rsp(inst) {
                    Some(op) => {
                        // if this push/pop instruction is about a callee saved register
                        // and the register is not used, we set the instruction as nop
                        if x86_64::is_callee_saved(op) && !used_callee_saved.contains(&op) {
                            trace!("removing instruction {:?} for save/restore unnecessary callee saved regs", inst);
                            regs_to_remove.push(op);
                            inst_to_remove.push(i);
                        }
                    }
                    None => {}
                }
            }
        }

        for i in inst_to_remove {
            self.set_inst_nop(i);
        }

        regs_to_remove
    }
qinsoon's avatar
qinsoon committed
523
524
525
526
527
528
529
530
531
532
533
534

    fn patch_frame_size(&mut self, size: usize) {
        let size = size.to_string();

        debug_assert!(size.len() <= FRAME_SIZE_PLACEHOLDER_LEN);

        for loc in self.frame_size_patchpoints.iter() {
            let ref mut inst = self.code[loc.line];

            string_utils::replace(&mut inst.code, loc.index, &size, size.len());
        }
    }
535
    
536
537
538
539
540
541
542
543
544
545
546
    fn emit(&self) -> Vec<u8> {
        let mut ret = vec![];
        
        for inst in self.code.iter() {
            ret.append(&mut inst.code.clone().into_bytes());
            ret.append(&mut "\n".to_string().into_bytes());
        }
        
        ret
    }
    
547
548
    fn trace_mc(&self) {
        trace!("");
549

550
551
        trace!("code for {}: \n", self.name);
        
552
553
        let n_insts = self.code.len();
        for i in 0..n_insts {
554
            self.trace_inst(i);
555
556
        }
        
557
558
559
560
561
562
        trace!("")      
    }
    
    fn trace_inst(&self, i: usize) {
        trace!("#{}\t{:30}\t\tdefine: {:?}\tuses: {:?}\tpred: {:?}\tsucc: {:?}", 
            i, self.code[i].code, self.get_inst_reg_defines(i), self.get_inst_reg_uses(i),
qinsoon's avatar
qinsoon committed
563
            self.code[i].preds, self.code[i].succs);
564
    }
565
    
566
    fn get_ir_block_livein(&self, block: &str) -> Option<&Vec<MuID>> {
qinsoon's avatar
qinsoon committed
567
568
569
570
        match self.blocks.get(block) {
            Some(ref block) => Some(&block.livein),
            None => None
        }
571
572
    }
    
573
    fn get_ir_block_liveout(&self, block: &str) -> Option<&Vec<MuID>> {
qinsoon's avatar
qinsoon committed
574
575
576
577
        match self.blocks.get(block) {
            Some(ref block) => Some(&block.liveout),
            None => None
        }
578
579
    }
    
580
    fn set_ir_block_livein(&mut self, block: &str, set: Vec<MuID>) {
qinsoon's avatar
qinsoon committed
581
582
        let block = self.blocks.get_mut(block).unwrap();
        block.livein = set;
583
584
585
    }
    
    fn set_ir_block_liveout(&mut self, block: &str, set: Vec<MuID>) {
qinsoon's avatar
qinsoon committed
586
587
        let block = self.blocks.get_mut(block).unwrap();
        block.liveout = set;
588
589
    }
    
qinsoon's avatar
qinsoon committed
590
591
    fn get_all_blocks(&self) -> Vec<MuName> {
        self.blocks.keys().map(|x| x.clone()).collect()
592
593
    }
    
594
    fn get_block_range(&self, block: &str) -> Option<ops::Range<usize>> {
qinsoon's avatar
qinsoon committed
595
596
        match self.blocks.get(block) {
            Some(ref block) => Some(block.start_inst..block.end_inst),
597
598
599
            None => None
        }
    }
600
601
}

qinsoon's avatar
qinsoon committed
602
603
604
605
606
607
608
#[derive(Clone, Debug)]
enum ASMBranchTarget {
    None,
    Conditional(MuName),
    Unconditional(MuName)
}

qinsoon's avatar
qinsoon committed
609
#[derive(Clone, Debug)]
qinsoon's avatar
qinsoon committed
610
struct ASMInst {
611
    code: String,
qinsoon's avatar
qinsoon committed
612
613
614
615
616
617
618
619

    defines: HashMap<MuID, Vec<ASMLocation>>,
    uses: HashMap<MuID, Vec<ASMLocation>>,

    is_mem_op_used: bool,
    preds: Vec<usize>,
    succs: Vec<usize>,
    branch: ASMBranchTarget
qinsoon's avatar
qinsoon committed
620
621
}

qinsoon's avatar
qinsoon committed
622
623
624
impl ASMInst {
    fn symbolic(line: String) -> ASMInst {
        ASMInst {
625
            code: line,
qinsoon's avatar
qinsoon committed
626
627
628
629
630
631
            defines: HashMap::new(),
            uses: HashMap::new(),
            is_mem_op_used: false,
            preds: vec![],
            succs: vec![],
            branch: ASMBranchTarget::None
632
633
634
        }
    }
    
qinsoon's avatar
qinsoon committed
635
636
637
638
639
640
641
642
643
    fn inst(
        inst: String,
        defines: HashMap<MuID, Vec<ASMLocation>>,
        uses: HashMap<MuID, Vec<ASMLocation>>,
        is_mem_op_used: bool,
        target: ASMBranchTarget
    ) -> ASMInst
    {
        ASMInst {
644
645
            code: inst,
            defines: defines,
qinsoon's avatar
qinsoon committed
646
647
648
649
650
            uses: uses,
            is_mem_op_used: is_mem_op_used,
            preds: vec![],
            succs: vec![],
            branch: target
651
652
        }
    }
653
    
qinsoon's avatar
qinsoon committed
654
655
    fn nop() -> ASMInst {
        ASMInst {
656
            code: "".to_string(),
qinsoon's avatar
qinsoon committed
657
658
659
660
661
662
            defines: HashMap::new(),
            uses: HashMap::new(),
            is_mem_op_used: false,
            preds: vec![],
            succs: vec![],
            branch: ASMBranchTarget::None
663
664
        }
    }
qinsoon's avatar
qinsoon committed
665
666
}

667
#[derive(Clone, Debug, PartialEq, Eq)]
qinsoon's avatar
qinsoon committed
668
669
670
struct ASMLocation {
    line: usize,
    index: usize,
qinsoon's avatar
qinsoon committed
671
672
    len: usize,
    oplen: usize,
qinsoon's avatar
qinsoon committed
673
674
675
}

impl ASMLocation {
qinsoon's avatar
qinsoon committed
676
    fn new(line: usize, index: usize, len: usize, oplen: usize) -> ASMLocation {
qinsoon's avatar
qinsoon committed
677
        ASMLocation{
qinsoon's avatar
qinsoon committed
678
            line: line,
qinsoon's avatar
qinsoon committed
679
            index: index,
qinsoon's avatar
qinsoon committed
680
681
            len: len,
            oplen: oplen
qinsoon's avatar
qinsoon committed
682
683
684
685
        }
    }
}

qinsoon's avatar
qinsoon committed
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
#[derive(Clone, Debug)]
/// [start_inst, end_inst)
struct ASMBlock {
    start_inst: usize,
    end_inst: usize,

    livein: Vec<MuID>,
    liveout: Vec<MuID>
}

impl ASMBlock {
    fn new() -> ASMBlock {
        ASMBlock {
            start_inst: usize::MAX,
            end_inst: usize::MAX,
            livein: vec![],
            liveout: vec![]
        }
    }
}

707
pub struct ASMCodeGen {
708
    cur: Option<Box<ASMCode>>
709
710
}

711
const REG_PLACEHOLDER_LEN : usize = 5;
qinsoon's avatar
qinsoon committed
712
713
714
715
716
717
lazy_static! {
    pub static ref REG_PLACEHOLDER : String = {
        let blank_spaces = [' ' as u8; REG_PLACEHOLDER_LEN];
        
        format!("%{}", str::from_utf8(&blank_spaces).unwrap())
    };
718
719
}

qinsoon's avatar
qinsoon committed
720
721
722
723
724
725
726
727
const FRAME_SIZE_PLACEHOLDER_LEN : usize = 10; // a frame is smaller than 1 << 10
lazy_static! {
    pub static ref FRAME_SIZE_PLACEHOLDER : String = {
        let blank_spaces = [' ' as u8; FRAME_SIZE_PLACEHOLDER_LEN];
        format!("{}", str::from_utf8(&blank_spaces).unwrap())
    };
}

728
729
impl ASMCodeGen {
    pub fn new() -> ASMCodeGen {
qinsoon's avatar
qinsoon committed
730
        ASMCodeGen {
731
            cur: None
qinsoon's avatar
qinsoon committed
732
733
734
735
736
737
738
739
740
741
742
        }
    }
    
    fn cur(&self) -> &ASMCode {
        self.cur.as_ref().unwrap()
    }
    
    fn cur_mut(&mut self) -> &mut ASMCode {
        self.cur.as_mut().unwrap()
    }
    
743
744
745
746
    fn line(&self) -> usize {
        self.cur().code.len()
    }
    
747
748
    fn add_asm_label(&mut self, code: String) {
        let l = self.line();
qinsoon's avatar
qinsoon committed
749
        self.cur_mut().code.push(ASMInst::symbolic(code));
750
751
    }
    
752
    fn add_asm_block_label(&mut self, code: String, block_name: MuName) {
753
        let l = self.line();
qinsoon's avatar
qinsoon committed
754
        self.cur_mut().code.push(ASMInst::symbolic(code));
755
756
    }
    
757
    fn add_asm_symbolic(&mut self, code: String){
qinsoon's avatar
qinsoon committed
758
        self.cur_mut().code.push(ASMInst::symbolic(code));
759
760
    }
    
761
762
    fn prepare_machine_regs(&self, regs: Iter<P<Value>>) -> Vec<MuID> {
        regs.map(|x| self.prepare_machine_reg(x)).collect()
qinsoon's avatar
qinsoon committed
763
    }
764
    
765
    fn add_asm_call(&mut self, code: String) {
qinsoon's avatar
qinsoon committed
766
        // a call instruction will use all the argument registers
qinsoon's avatar
qinsoon committed
767
768
769
770
771
772
773
        let mut uses : HashMap<MuID, Vec<ASMLocation>> = HashMap::new();
        for reg in x86_64::ARGUMENT_GPRs.iter() {
            uses.insert(reg.id(), vec![]);
        }
        for reg in x86_64::ARGUMENT_FPRs.iter() {
            uses.insert(reg.id(), vec![]);
        }
qinsoon's avatar
qinsoon committed
774
775

        // defines: return registers
qinsoon's avatar
qinsoon committed
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
        let mut defines : HashMap<MuID, Vec<ASMLocation>> = HashMap::new();
        for reg in x86_64::RETURN_GPRs.iter() {
            defines.insert(reg.id(), vec![]);
        }
        for reg in x86_64::RETURN_FPRs.iter() {
            defines.insert(reg.id(), vec![]);
        }
        for reg in x86_64::CALLER_SAVED_GPRs.iter() {
            if !defines.contains_key(&reg.id()) {
                defines.insert(reg.id(), vec![]);
            }
        }
        for reg in x86_64::CALLER_SAVED_FPRs.iter() {
            if !defines.contains_key(&reg.id()) {
                defines.insert(reg.id(), vec![]);
            }
        }
793
          
qinsoon's avatar
qinsoon committed
794
        self.add_asm_inst(code, defines, uses, false);
795
796
797
    }
    
    fn add_asm_ret(&mut self, code: String) {
798
799
800
801
802
        // return instruction does not use anything (not RETURN REGS)
        // otherwise it will keep RETURN REGS alive
        // and if there is no actual move into RETURN REGS, it will keep RETURN REGS for alive for very long
        // and prevents anything using those regsiters
        self.add_asm_inst(code, hashmap!{}, hashmap!{}, false);
803
804
    }
    
805
    fn add_asm_branch(&mut self, code: String, target: MuName) {
qinsoon's avatar
qinsoon committed
806
        self.add_asm_inst_internal(code, hashmap!{}, hashmap!{}, false, ASMBranchTarget::Unconditional(target));
807
808
    }
    
809
    fn add_asm_branch2(&mut self, code: String, target: MuName) {
qinsoon's avatar
qinsoon committed
810
        self.add_asm_inst_internal(code, hashmap!{}, hashmap!{}, false, ASMBranchTarget::Conditional(target));
811
812
813
814
815
    }
    
    fn add_asm_inst(
        &mut self, 
        code: String, 
qinsoon's avatar
qinsoon committed
816
817
818
819
820
821
822
823
824
825
826
827
828
829
        defines: HashMap<MuID, Vec<ASMLocation>>,
        uses: HashMap<MuID, Vec<ASMLocation>>,
        is_using_mem_op: bool)
    {
        self.add_asm_inst_internal(code, defines, uses, is_using_mem_op, ASMBranchTarget::None)
    }

    fn add_asm_inst_internal(
        &mut self,
        code: String,
        defines: HashMap<MuID, Vec<ASMLocation>>,
        uses: HashMap<MuID, Vec<ASMLocation>>,
        is_using_mem_op: bool,
        target: ASMBranchTarget)
830
    {
831
832
        let line = self.line();
        trace!("asm: {}", code);
qinsoon's avatar
qinsoon committed
833
834
        trace!("     defines: {:?}", defines);
        trace!("     uses: {:?}", uses);
835
        let mc = self.cur_mut();
qinsoon's avatar
qinsoon committed
836

837
        // put the instruction
qinsoon's avatar
qinsoon committed
838
        mc.code.push(ASMInst::inst(code, defines, uses, is_using_mem_op, target));
qinsoon's avatar
qinsoon committed
839
840
    }
    
841
    fn prepare_reg(&self, op: &P<Value>, loc: usize) -> (String, MuID, ASMLocation) {
842
843
844
845
846
847
848
        if cfg!(debug_assertions) {
            match op.v {
                Value_::SSAVar(_) => {},
                _ => panic!("expecting register op")
            }
        }
        
849
850
        let str = self.asm_reg_op(op);
        let len = str.len();
qinsoon's avatar
qinsoon committed
851
852
853
854
855
856
857
858
859
860
861
862
863
864
        (str, op.extract_ssa_id().unwrap(), ASMLocation::new(self.line(), loc, len, check_op_len(op)))
    }

    fn prepare_fpreg(&self, op: &P<Value>, loc: usize) -> (String, MuID, ASMLocation) {
        if cfg!(debug_assertions) {
            match op.v {
                Value_::SSAVar(_) => {},
                _ => panic!("expecting register op")
            }
        }

        let str = self.asm_reg_op(op);
        let len = str.len();
        (str, op.extract_ssa_id().unwrap(), ASMLocation::new(self.line(), loc, len, 64))
865
866
867
    }
    
    fn prepare_machine_reg(&self, op: &P<Value>) -> MuID {
868
869
870
871
872
873
874
        if cfg!(debug_assertions) {
            match op.v {
                Value_::SSAVar(_) => {},
                _ => panic!("expecting machine register op")
            }
        }        
        
875
        op.extract_ssa_id().unwrap()
876
    }
877
    
878
    #[allow(unused_assignments)]
qinsoon's avatar
qinsoon committed
879
    fn prepare_mem(&self, op: &P<Value>, loc: usize) -> (String, HashMap<MuID, Vec<ASMLocation>>) {
880
881
882
        if cfg!(debug_assertions) {
            match op.v {
                Value_::Memory(_) => {},
qinsoon's avatar
qinsoon committed
883
                _ => panic!("expecting memory op")
884
885
            }
        }        
qinsoon's avatar
qinsoon committed
886

887
888
889
890
        let mut ids : Vec<MuID> = vec![];
        let mut locs : Vec<ASMLocation> = vec![];
        let mut result_str : String = "".to_string();
        
891
        let mut loc_cursor : usize = loc;
892
893
894
895
896
897
898
899
900
901
902
        
        match op.v {
            // offset(base,index,scale)
            Value_::Memory(MemoryLocation::Address{ref base, ref offset, ref index, scale}) => {
                // deal with offset
                if offset.is_some() {
                    let offset = offset.as_ref().unwrap();
                    
                    match offset.v {
                        Value_::SSAVar(id) => {
                            // temp as offset
qinsoon's avatar
qinsoon committed
903
                            let (str, id, loc) = self.prepare_reg(offset, loc_cursor);
904
905
906
907
908
909
910
911
                            
                            result_str.push_str(&str);
                            ids.push(id);
                            locs.push(loc);
                            
                            loc_cursor += str.len();
                        },
                        Value_::Constant(Constant::Int(val)) => {
qinsoon's avatar
qinsoon committed
912
                            let str = (val as i32).to_string();
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
                            
                            result_str.push_str(&str);
                            loc_cursor += str.len();
                        },
                        _ => panic!("unexpected offset type: {:?}", offset)
                    }
                }
                
                result_str.push('(');
                loc_cursor += 1; 
                
                // deal with base, base is ssa
                let (str, id, loc) = self.prepare_reg(base, loc_cursor);
                result_str.push_str(&str);
                ids.push(id);
                locs.push(loc);
                loc_cursor += str.len();
                
                // deal with index (ssa or constant)
                if index.is_some() {
                    result_str.push(',');
                    loc_cursor += 1; // plus 1 for ,                    
                    
                    let index = index.as_ref().unwrap();
                    
                    match index.v {
                        Value_::SSAVar(id) => {
                            // temp as offset
                            let (str, id, loc) = self.prepare_reg(index, loc_cursor);
                            
                            result_str.push_str(&str);
                            ids.push(id);
                            locs.push(loc);
                            
                            loc_cursor += str.len();
                        },
                        Value_::Constant(Constant::Int(val)) => {
qinsoon's avatar
qinsoon committed
950
                            let str = (val as i32).to_string();
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
                            
                            result_str.push_str(&str);
                            loc_cursor += str.len();
                        },
                        _ => panic!("unexpected index type: {:?}", index)
                    }
                    
                    // scale
                    if scale.is_some() {
                        result_str.push(',');
                        loc_cursor += 1;
                        
                        let scale = scale.unwrap();
                        let str = scale.to_string();
                        
                        result_str.push_str(&str);
                        loc_cursor += str.len();
                    }
                }
                
                result_str.push(')');
                loc_cursor += 1;
            },
974
975
            Value_::Memory(MemoryLocation::Symbolic{ref base, ref label}) => {
                result_str.push_str(&symbol(label.clone()));
976
977
978
979
980
981
982
983
984
985
986
                loc_cursor += label.len();
                
                if base.is_some() {
                    result_str.push('(');
                    loc_cursor += 1;
                    
                    let (str, id, loc) = self.prepare_reg(base.as_ref().unwrap(), loc_cursor);
                    result_str.push_str(&str);
                    ids.push(id);
                    locs.push(loc);
                    loc_cursor += str.len();
qinsoon's avatar
qinsoon committed
987

988
                    result_str.push(')');
qinsoon's avatar
qinsoon committed
989
                    loc_cursor += 1;
990
991
992
993
                }
            },
            _ => panic!("expect mem location as value")
        }
qinsoon's avatar
qinsoon committed
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011

        let uses : HashMap<MuID, Vec<ASMLocation>> = {
            let mut map : HashMap<MuID, Vec<ASMLocation>> = hashmap!{};
            for i in 0..ids.len() {
                let id = ids[i];
                let loc = locs[i].clone();

                if map.contains_key(&id) {
                    map.get_mut(&id).unwrap().push(loc);
                } else {
                    map.insert(id, vec![loc]);
                }
            }
            map
        };


        (result_str, uses)
1012
    }
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022

    fn prepare_imm(&self, op: i32, len: usize) -> i32 {
        match len {
            64 => op,
            32 => op,
            16 => op as i16 as i32,
            8  => op as i8  as i32,
            _ => unimplemented!()
        }
    }
1023
    
qinsoon's avatar
qinsoon committed
1024
1025
    fn asm_reg_op(&self, op: &P<Value>) -> String {
        let id = op.extract_ssa_id().unwrap();
qinsoon's avatar
qinsoon committed
1026
        if id < MACHINE_ID_END {
qinsoon's avatar
qinsoon committed
1027
            // machine reg
1028
            format!("%{}", op.name().unwrap())
qinsoon's avatar
qinsoon committed
1029
1030
1031
1032
1033
1034
        } else {
            // virtual register, use place holder
            REG_PLACEHOLDER.clone()
        }
    }
    
qinsoon's avatar
qinsoon committed
1035
1036
    fn mangle_block_label(&self, label: MuName) -> String {
        format!("{}_{}", self.cur().name, label)
1037
    }
1038
1039
1040
1041
    
    fn control_flow_analysis(&mut self) {
        // control flow analysis
        let n_insts = self.line();
qinsoon's avatar
qinsoon committed
1042

1043
        let code = self.cur_mut();
qinsoon's avatar
qinsoon committed
1044
        let ref blocks = code.blocks;
qinsoon's avatar
qinsoon committed
1045
        let ref mut asm = code.code;
qinsoon's avatar
qinsoon committed
1046
1047
1048
1049
1050
1051
1052
1053

        let block_start = {
            let mut ret = vec![];
            for block in blocks.values() {
                ret.push(block.start_inst);
            }
            ret
        };
1054
1055
1056
        
        for i in 0..n_insts {
            // determine predecessor - if cur is not block start, its predecessor is previous insts
qinsoon's avatar
qinsoon committed
1057
1058
            let is_block_start = block_start.contains(&i);
            if !is_block_start {
1059
                if i > 0 {
1060
1061
                    trace!("inst {}: not a block start", i);
                    trace!("inst {}: set PREDS as previous inst {}", i, i-1);
qinsoon's avatar
qinsoon committed
1062
                    asm[i].preds.push(i - 1);
1063
1064
1065
1066
1067
1068
1069
                }
            } else {
                // if cur is a branch target, we already set its predecessor
                // if cur is a fall-through block, we set it in a sanity check pass
            }
            
            // determine successor
qinsoon's avatar
qinsoon committed
1070
1071
1072
1073
1074
1075
            let branch = asm[i].branch.clone();
            match branch {
                ASMBranchTarget::Unconditional(ref target) => {
                    // branch to target
                    trace!("inst {}: is a branch to {}", i, target);

qinsoon's avatar
qinsoon committed
1076
                    let target_n = code.blocks.get(target).unwrap().start_inst;
qinsoon's avatar
qinsoon committed
1077
1078
1079
1080
                    trace!("inst {}: branch target index is {}", i, target_n);

                    // cur inst's succ is target
                    trace!("inst {}: set SUCCS as branch target {}", i, target_n);
qinsoon's avatar
qinsoon committed
1081
                    asm[i].succs.push(target_n);
qinsoon's avatar
qinsoon committed
1082
1083
1084

                    // target's pred is cur
                    trace!("inst {}: set PREDS as branch source {}", target_n, i);
qinsoon's avatar
qinsoon committed
1085
                    asm[target_n].preds.push(i);
qinsoon's avatar
qinsoon committed
1086
1087
                },
                ASMBranchTarget::Conditional(ref target) => {
1088
                    // branch to target
1089
                    trace!("inst {}: is a cond branch to {}", i, target);
qinsoon's avatar
qinsoon committed
1090

qinsoon's avatar
qinsoon committed
1091
                    let target_n = code.blocks.get(target).unwrap().start_inst;
1092
                    trace!("inst {}: branch target index is {}", i, target_n);
qinsoon's avatar
qinsoon committed
1093

1094
                    // cur insts' succ is target and next inst
qinsoon's avatar
qinsoon committed
1095
                    asm[i].succs.push(target_n);
1096
                    trace!("inst {}: set SUCCS as branch target {}", i, target_n);
1097
                    if i < n_insts - 1 {
qinsoon's avatar
qinsoon committed
1098
1099
                        trace!("inst {}: set SUCCS as next inst", i + 1);
                        asm[i].succs.push(i + 1);
1100
                    }
qinsoon's avatar
qinsoon committed
1101

1102
                    // target's pred is cur
qinsoon's avatar
qinsoon committed
1103
1104
                    asm[target_n].preds.push(i);
                    trace!("inst {}: set PREDS as {}", target_n, i);
qinsoon's avatar
qinsoon committed
1105
1106
                },
                ASMBranchTarget::None => {
1107
                    // not branch nor cond branch, succ is next inst
1108
                    trace!("inst {}: not a branch inst", i);
1109
                    if i < n_insts - 1 {
1110
                        trace!("inst {}: set SUCCS as next inst {}", i, i + 1);
qinsoon's avatar
qinsoon committed
1111
                        asm[i].succs.push(i + 1);
1112
1113
                    }
                }
qinsoon's avatar
qinsoon committed
1114
            }
1115
1116
1117
1118
        }
        
        // a sanity check for fallthrough blocks
        for i in 0..n_insts {
qinsoon's avatar
qinsoon committed
1119
1120
            if i != 0 && asm[i].preds.len() == 0 {
                asm[i].preds.push(i - 1);
1121
1122
1123
            }
        }        
    }
1124
1125
1126
1127

    fn finish_code_sequence_asm(&mut self) -> Box<ASMCode> {
        self.cur.take().unwrap()
    }
1128

qinsoon's avatar
qinsoon committed
1129
1130
    fn internal_binop_no_def_r_r(&mut self, inst: &str, op1: &P<Value>, op2: &P<Value>) {
        let len = check_op_len(op1);
1131

qinsoon's avatar
qinsoon committed
1132
1133
1134
        // with postfix
        let inst = inst.to_string() + &op_postfix(len);
        trace!("emit: {} {} {}", inst, op1, op2);
1135

qinsoon's avatar
qinsoon committed
1136
1137
        let (reg1, id1, loc1) = self.prepare_reg(op1, inst.len() + 1);
        let (reg2, id2, loc2) = self.prepare_reg(op2, inst.len() + 1 + reg1.len() + 1);
1138

qinsoon's avatar
qinsoon committed
1139
        let asm = format!("{} {},{}", inst, reg1, reg2);
1140

qinsoon's avatar
qinsoon committed
1141
1142
1143
1144
1145
1146
1147
        self.add_asm_inst(
            asm,
            hashmap!{},
            {
                if id1 == id2 {
                    hashmap!{
                        id1 => vec![loc1, loc2]
1148
                    }
qinsoon's avatar
qinsoon committed
1149
1150
1151
1152
1153
1154
1155
1156
1157
                } else {
                    hashmap!{
                        id1 => vec![loc1],
                        id2 => vec![loc2]
                    }
                }
            },
            false
        )
1158
1159
    }

qinsoon's avatar
qinsoon committed
1160
1161
    fn internal_binop_no_def_imm_r(&mut self, inst: &str, op1: i32, op2: &P<Value>) {
        let len = check_op_len(op2);
1162

qinsoon's avatar
qinsoon committed
1163
1164
        let inst = inst.to_string() + &op_postfix(len);
        trace!("emit: {} {} {}", inst, op1, op2);
1165

1166
1167
        let imm = self.prepare_imm(op1, len);
        let (reg2, id2, loc2) = self.prepare_reg(op2, inst.len() + 1 + 1 + imm.to_string().len() + 1);
1168

1169
        let asm = format!("{} ${},{}", inst, imm, reg2);
1170

qinsoon's avatar
qinsoon committed
1171
1172
1173
1174
1175
1176
1177
1178
        self.add_asm_inst(
            asm,
            hashmap!{},
            hashmap!{
                id2 => vec![loc2]
            },
            false
        )
1179
1180
    }

qinsoon's avatar
qinsoon committed
1181
1182
    fn internal_binop_no_def_mem_r(&mut self, inst: &str, op1: &P<Value>, op2: &P<Value>) {
        let len = check_op_len(op2);
1183

qinsoon's avatar
qinsoon committed
1184
1185
        let inst = inst.to_string() + &op_postfix(len);
        trace!("emit: {} {} {}", inst, op1, op2);
1186

qinsoon's avatar
qinsoon committed
1187
1188
        let (mem, mut uses)  = self.prepare_mem(op1, inst.len() + 1);
        let (reg, id1, loc1) = self.prepare_reg(op2, inst.len() + 1 + mem.len() + 1);
1189

qinsoon's avatar
qinsoon committed
1190
        let asm = format!("{} {},{}", inst, mem, reg);
1191

qinsoon's avatar
qinsoon committed
1192
1193
1194
1195
1196
1197
        // merge use vec
        if uses.contains_key(&id1) {
            let mut locs = uses.get_mut(&id1).unwrap();
            vec_utils::add_unique(locs, loc1.clone());
        } else {
            uses.insert(id1, vec![loc1]);
1198
        }
qinsoon's avatar
qinsoon committed
1199
1200
1201
1202
1203
1204
1205

        self.add_asm_inst(
            asm,
            hashmap!{},
            uses,
            true
        )
1206
1207
    }

qinsoon's avatar
qinsoon committed
1208
1209
    fn internal_binop_no_def_r_mem(&mut self, inst: &str, op1: &P<Value>, op2: &P<Value>) {
        let len = check_op_len(op1);
1210

qinsoon's avatar
qinsoon committed
1211
1212
        let inst = inst.to_string() + &op_postfix(len);
        trace!("emit: {} {} {}", inst, op1, op2);
1213

qinsoon's avatar
qinsoon committed
1214
1215
        let (mem, mut uses) = self.prepare_mem(op2, inst.len() + 1);
        let (reg, id1, loc1) = self.prepare_reg(op1, inst.len() + 1 + mem.len() + 1);
1216