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

15 16 17 18 19 20 21 22 23 24 25 26 27
use ast::ir::*;
use ast::ptr::*;
use ast::inst::*;
use vm::VM;

use compiler::CompilerPass;
use std::any::Any;
use std::collections::HashMap;

pub struct Inlining {
    name: &'static str,

    // whether a function version should be inlined
28
    should_inline: HashMap<MuID, bool>
29 30
}

qinsoon's avatar
qinsoon committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
impl CompilerPass for Inlining {
    fn name(&self) -> &'static str {
        self.name
    }

    fn as_any(&self) -> &Any {
        self
    }

    fn visit_function(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
        if vm.vm_options.flag_disable_inline {
            info!("inlining is disabled");
            return;
        }

        if self.check(vm, func) {
            self.inline(vm, func);
            debug!("after inlining: {:?}", func);
        }
    }
}

53 54
impl Inlining {
    pub fn new() -> Inlining {
55
        Inlining {
56
            name: "Inlining",
57
            should_inline: HashMap::new()
58 59 60
        }
    }

qinsoon's avatar
qinsoon committed
61
    /// checks whether we need to rewrite the function because of inlining
62
    fn check(&mut self, vm: &VM, func: &mut MuFunctionVersion) -> bool {
qinsoon's avatar
qinsoon committed
63
        debug!("check inline");
qinsoon's avatar
qinsoon committed
64 65 66

        // should_inline will store all the calls from this function,
        // and whether they should be inlined
67 68
        self.should_inline.clear();

qinsoon's avatar
qinsoon committed
69 70
        let mut inline_something = false;

qinsoon's avatar
qinsoon committed
71
        // check each call from this function
qinsoon's avatar
qinsoon committed
72
        for func_id in func.get_static_call_edges().values() {
qinsoon's avatar
qinsoon committed
73 74
            // check a single callsite, whether it should be inlined
            // the result is returned as boolean, and also written into 'should_inline'
75
            let should_inline_this = self.check_should_inline_func(*func_id, func.func_id, vm);
qinsoon's avatar
qinsoon committed
76 77 78 79 80 81
            inline_something = inline_something || should_inline_this;
        }

        inline_something
    }

qinsoon's avatar
qinsoon committed
82
    /// checks whether we should inline the caller into the callee
83 84 85 86 87 88
    fn check_should_inline_func(&mut self, callee: MuID, caller: MuID, vm: &VM) -> bool {
        // recursive call, do not inline
        if callee == caller {
            return false;
        }

qinsoon's avatar
qinsoon committed
89
        let funcs_guard = vm.funcs().read().unwrap();
90
        let func = match funcs_guard.get(&callee) {
qinsoon's avatar
qinsoon committed
91
            Some(func) => func.read().unwrap(),
92
            None => panic!("callee {} is undeclared", callee)
qinsoon's avatar
qinsoon committed
93 94 95 96 97 98 99 100 101 102
        };
        let fv_id = match func.cur_ver {
            Some(fv_id) => fv_id,
            None => {
                // the funtion is not defined
                info!("the function is undefined, we cannot inline it. ");
                return false;
            }
        };

qinsoon's avatar
qinsoon committed
103 104 105 106
        // if we have checked this callee before, we use the same result
        // (this is not optimal though. The inline criteria we use at the moment
        // do not take caller size growth into consideration, so we will
        // get the same result anyway. )
qinsoon's avatar
qinsoon committed
107 108
        match self.should_inline.get(&fv_id) {
            Some(flag) => {
109
                trace!("func {} should be inlined (checked before)", callee);
qinsoon's avatar
qinsoon committed
110 111 112 113 114 115 116 117
                return *flag;
            }
            None => {}
        }

        let fv_guard = vm.func_vers().read().unwrap();
        let fv = fv_guard.get(&fv_id).unwrap().read().unwrap();

qinsoon's avatar
qinsoon committed
118
        // if the function is forced inline, we inline it
qinsoon's avatar
qinsoon committed
119
        if fv.force_inline {
120
            trace!("func {} is forced as inline function", callee);
qinsoon's avatar
qinsoon committed
121 122 123 124
            return true;
        }

        // some heuristics here to decide if we should inline the function
125
        let n_insts = estimate_insts(&fv);
qinsoon's avatar
qinsoon committed
126 127
        let out_calls = fv.get_static_call_edges();
        let has_throw = fv.has_throw();
128
        let has_tailcall = fv.has_tailcall();
qinsoon's avatar
qinsoon committed
129

qinsoon's avatar
qinsoon committed
130 131 132 133
        // simple heuristic here:
        // * estimated machine insts are fewer than 10 insts
        // * leaf in call graph (no out calls)
        // * no throw (otherwise we will need to rearrange catch)
134
        let should_inline = n_insts <= 25 && out_calls.len() == 0 && !has_throw && !has_tailcall;
qinsoon's avatar
qinsoon committed
135

136
        trace!("func {} has {} insts (estimated)", callee, n_insts);
qinsoon's avatar
qinsoon committed
137 138 139 140
        trace!("     has {} out calls", out_calls.len());
        trace!("     has throws? {}", has_throw);
        trace!("SO func should be inlined? {}", should_inline);

141
        self.should_inline.insert(callee, should_inline);
qinsoon's avatar
qinsoon committed
142 143

        should_inline
144 145
    }

qinsoon's avatar
qinsoon committed
146
    /// inlines the callee that are marked as 'should inline'
147
    fn inline(&mut self, vm: &VM, func: &mut MuFunctionVersion) {
qinsoon's avatar
qinsoon committed
148 149 150 151 152 153 154
        debug!("inlining for Function {}", func);

        let call_edges = func.get_static_call_edges();

        let mut f_content = func.content.as_mut().unwrap();
        let ref mut f_context = func.context;

155
        let mut new_blocks: Vec<Block> = vec![];
qinsoon's avatar
qinsoon committed
156

157
        for (_, block) in f_content.blocks.iter() {
qinsoon's avatar
qinsoon committed
158 159 160 161
            // clone curent block, and clear its instructions
            let mut cur_block = block.clone();
            cur_block.content.as_mut().unwrap().body.clear();

162 163
            let block = block.clone();

qinsoon's avatar
qinsoon committed
164 165
            // iterate through instructions
            for inst in block.content.unwrap().body {
166
                trace!("check inst: {}", inst);
qinsoon's avatar
qinsoon committed
167 168
                let inst_id = inst.id();
                if call_edges.contains_key(&inst_id) {
169
                    let call_target = call_edges.get(&inst_id).unwrap();
170 171 172
                    if self.should_inline.contains_key(call_target) &&
                        *self.should_inline.get(call_target).unwrap()
                    {
173 174 175 176 177
                        trace!("inserting inlined function at {}", inst);

                        // from TreeNode into Inst (we do not need old TreeNode)
                        let inst = inst.into_inst().unwrap();

qinsoon's avatar
qinsoon committed
178
                        // inline expansion starts here
179

qinsoon's avatar
qinsoon committed
180
                        // getting the function being inlined
181 182
                        let inlined_func = *call_edges.get(&inst.id()).unwrap();
                        trace!("function being inlined is {}", inlined_func);
qinsoon's avatar
vm.rs  
qinsoon committed
183
                        let inlined_fvid = match vm.get_cur_version_for_func(inlined_func) {
184
                            Some(fvid) => fvid,
185 186
                            None => {
                                panic!(
qinsoon's avatar
qinsoon committed
187 188
                                    "cannot resolve current version of Func {}, \
                                     which is supposed to be inlined",
189 190 191
                                    inlined_func
                                )
                            }
192 193
                        };
                        let inlined_fvs_guard = vm.func_vers().read().unwrap();
194 195 196 197 198 199 200 201 202 203
                        let inlined_fv_lock = inlined_fvs_guard.get(&inlined_fvid).unwrap();
                        let inlined_fv_guard = inlined_fv_lock.read().unwrap();
                        trace!(
                            "orig_content: {:?}",
                            inlined_fv_guard.get_orig_ir().unwrap()
                        );
                        trace!(
                            "content     : {:?}",
                            inlined_fv_guard.content.as_ref().unwrap()
                        );
qinsoon's avatar
qinsoon committed
204 205
                        // creates a new block ID
                        // which will be the entry block for the inlined function
206 207
                        let new_inlined_entry_id = vm.next_id();

qinsoon's avatar
qinsoon committed
208
                        // change current call instruction to a branch
209
                        trace!("turning CALL instruction into a branch");
210
                        let ref ops = inst.ops;
211
                        match inst.v {
212 213 214
                            Instruction_::ExprCall { ref data, .. } => {
                                let arg_nodes: Vec<P<TreeNode>> =
                                    data.args.iter().map(|x| ops[*x].clone()).collect();
215 216
                                let arg_indices: Vec<OpIndex> = (0..arg_nodes.len()).collect();

217
                                let branch = TreeNode::new_boxed_inst(Instruction {
218 219
                                    hdr: inst.hdr.clone(),
                                    value: None,
220
                                    ops: arg_nodes.clone(),
221
                                    v: Instruction_::Branch1(Destination {
qinsoon's avatar
qinsoon committed
222
                                        // this block doesnt exist yet, we will create it later
223
                                        target: new_inlined_entry_id,
224 225 226
                                        args: arg_indices
                                            .iter()
                                            .map(|x| DestArg::Normal(*x))
227 228
                                            .collect()
                                    })
229 230 231 232 233 234 235 236 237
                                });
                                trace!("branch inst: {}", branch);

                                // add branch to current block
                                cur_block.content.as_mut().unwrap().body.push(branch);

                                // finish current block
                                new_blocks.push(cur_block.clone());

238 239 240
                                // creates a new block after inlined part,
                                // which will receive results from inlined function
                                let old_name = cur_block.name();
241 242
                                let new_name =
                                    format!("{}_cont_after_inline_{}", old_name, inst_id);
243
                                trace!("create continue block for EXPRCALL/CCALL: {}", &new_name);
244 245 246
                                cur_block =
                                    Block::new(MuEntityHeader::named(vm.next_id(), new_name));
                                cur_block.content = Some(BlockContent {
247 248 249 250 251 252 253 254 255
                                    args: {
                                        if inst.value.is_none() {
                                            vec![]
                                        } else {
                                            inst.value.unwrap()
                                        }
                                    },
                                    exn_arg: None,
                                    body: vec![],
256
                                    keepalives: None
257
                                });
258
                                vm.set_name(cur_block.as_entity());
259 260

                                // deal with the inlined function
261 262 263 264 265
                                copy_inline_blocks(
                                    &mut new_blocks,
                                    cur_block.id(),
                                    inlined_fv_guard.get_orig_ir().unwrap(),
                                    new_inlined_entry_id,
266
                                    vm
267
                                );
268
                                copy_inline_context(f_context, &inlined_fv_guard.context);
269 270 271 272
                            }

                            Instruction_::Call {
                                ref data,
273
                                ref resume
274 275 276
                            } => {
                                let arg_nodes: Vec<P<TreeNode>> =
                                    data.args.iter().map(|x| ops[*x].clone()).collect();
277 278
                                let arg_indices: Vec<OpIndex> = (0..arg_nodes.len()).collect();

279
                                let branch = Instruction {
280 281
                                    hdr: inst.hdr.clone(),
                                    value: None,
282
                                    ops: arg_nodes,
283
                                    v: Instruction_::Branch1(Destination {
284
                                        target: new_inlined_entry_id,
285 286 287
                                        args: arg_indices
                                            .iter()
                                            .map(|x| DestArg::Normal(*x))
288 289
                                            .collect()
                                    })
290 291 292
                                };

                                // add branch to current block
293 294 295 296 297 298
                                cur_block
                                    .content
                                    .as_mut()
                                    .unwrap()
                                    .body
                                    .push(TreeNode::new_boxed_inst(branch));
299

300 301 302
                                // next block
                                let mut next_block = resume.normal_dest.target;

303
                                // if normal_dest expects different number of arguments
qinsoon's avatar
qinsoon committed
304 305
                                // other than the inlined function returns, we need
                                // an intermediate block to pass extra arguments
306 307 308
                                if resume.normal_dest.args.len() !=
                                    inlined_fv_guard.sig.ret_tys.len()
                                {
309
                                    debug!("need an extra block for passing normal dest arguments");
310
                                    let int_block_name = format!("inline_{}_arg_pass", inst_id);
311
                                    let mut intermediate_block = Block::new(
312
                                        MuEntityHeader::named(vm.next_id(), int_block_name)
313
                                    );
314
                                    vm.set_name(intermediate_block.as_entity());
315 316

                                    // branch to normal_dest with normal_dest arguments
317 318
                                    let normal_dest_args =
                                        resume.normal_dest.get_arguments_as_node(&ops);
319 320 321 322 323
                                    let normal_dest_args_len = normal_dest_args.len();

                                    let branch = Instruction {
                                        hdr: MuEntityHeader::unnamed(vm.next_id()),
                                        value: None,
324
                                        ops: normal_dest_args,
325 326
                                        v: Instruction_::Branch1(Destination {
                                            target: resume.normal_dest.target,
327 328
                                            args: (0..normal_dest_args_len)
                                                .map(|x| DestArg::Normal(x))
329 330
                                                .collect()
                                        })
331 332 333 334 335 336
                                    };

                                    intermediate_block.content = Some(BlockContent {
                                        args: {
                                            match inst.value {
                                                Some(ref vec) => vec.clone(),
337
                                                None => vec![]
338 339 340 341
                                            }
                                        },
                                        exn_arg: None,
                                        body: vec![TreeNode::new_boxed_inst(branch)],
342
                                        keepalives: None
343 344 345 346 347 348
                                    });

                                    trace!("extra block: {:?}", intermediate_block);

                                    next_block = intermediate_block.id();
                                    new_blocks.push(intermediate_block);
349 350 351
                                }

                                // deal with inlined function
352 353 354 355 356
                                copy_inline_blocks(
                                    &mut new_blocks,
                                    next_block,
                                    inlined_fv_guard.get_orig_ir().unwrap(),
                                    new_inlined_entry_id,
357
                                    vm
358
                                );
359
                                copy_inline_context(f_context, &inlined_fv_guard.context);
360
                            }
361

362
                            _ => panic!("unexpected callsite: {}", inst)
363 364 365
                        }
                    } else {
                        cur_block.content.as_mut().unwrap().body.push(inst.clone());
qinsoon's avatar
qinsoon committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
                    }
                } else {
                    cur_block.content.as_mut().unwrap().body.push(inst.clone());
                }
            }

            new_blocks.push(cur_block);
        }

        f_content.blocks.clear();
        for blk in new_blocks {
            f_content.blocks.insert(blk.id(), blk);
        }
    }
}

qinsoon's avatar
qinsoon committed
382
/// copies blocks from callee to caller, with specified entry block and return block
383 384 385 386 387
fn copy_inline_blocks(
    caller: &mut Vec<Block>,
    ret_block: MuID,
    callee: &FunctionContent,
    entry_block: MuID,
388
    vm: &VM
389
) {
390
    trace!("trying to copy inlined function blocks to caller");
391 392

    // old id -> new id
393
    let mut block_map: HashMap<MuID, MuID> = HashMap::new();
394 395 396 397 398 399 400 401 402

    for block in callee.blocks.values() {
        if block.id() == callee.entry {
            block_map.insert(block.id(), entry_block);
        } else {
            block_map.insert(block.id(), vm.next_id());
        }
    }

403
    let fix_dest = |dest: Destination| {
404 405
        Destination {
            target: *block_map.get(&dest.target).unwrap(),
406
            args: dest.args
407 408 409
        }
    };

410
    let fix_resume = |resume: ResumptionData| {
411 412
        ResumptionData {
            normal_dest: fix_dest(resume.normal_dest),
413
            exn_dest: fix_dest(resume.exn_dest)
414 415 416
        }
    };

417 418 419
    for old_block in callee.blocks.values() {
        let old_id = old_block.id();
        let new_id = *block_map.get(&old_block.id()).unwrap();
420
        let mut block = Block {
421 422
            hdr: MuEntityHeader::named(
                new_id,
423
                format!("{}:inlinedblock.#{}", old_block.name(), new_id)
424
            ),
425
            content: Some(old_block.content.as_ref().unwrap().clone_empty()),
426
            trace_hint: TraceHint::None,
427
            control_flow: ControlFlow::default()
428
        };
qinsoon's avatar
qinsoon committed
429

qinsoon's avatar
[wip]  
qinsoon committed
430 431
        trace!("starts copying instruction from {} to {}", old_id, new_id);

432
        // Create the new blocks contents
qinsoon's avatar
qinsoon committed
433
        {
434
            let old_block_content = old_block.content.as_ref().unwrap();
qinsoon's avatar
qinsoon committed
435
            let block_content = block.content.as_mut().unwrap();
436

437 438 439 440 441 442 443 444 445 446 447 448
            // Copy the old_block contents (minus the last one)
            for i in 0..old_block_content.body.len() - 1 {
                block_content.body.push(match old_block_content.body[i].v {
                    TreeNode_::Instruction(ref inst) => {
                        TreeNode::new_boxed_inst(inst.clone_with_id(vm.next_id()))
                    }
                    _ => panic!("expect instruction as block body")
                });
            }

            // check its last instruction
            let last_inst = old_block_content.body.last().unwrap();
449 450 451 452 453 454
            // every inst should have a unique ID
            let inst_new_id = vm.next_id();
            let last_inst_clone = match last_inst.v {
                TreeNode_::Instruction(ref inst) => {
                    TreeNode::new_boxed_inst(inst.clone_with_id(inst_new_id))
                }
455
                _ => panic!("expect instruction as block body")
456
            };
qinsoon's avatar
qinsoon committed
457

458 459
            match &last_inst.v {
                &TreeNode_::Instruction(ref inst) => {
qinsoon's avatar
[wip]  
qinsoon committed
460 461
                    trace!("last instruction: {}", inst);

462
                    let hdr = inst.hdr.clone_with_id(inst_new_id);
463 464 465
                    let ref value = inst.value;
                    let ref ops = inst.ops;
                    let ref v = inst.v;
qinsoon's avatar
qinsoon committed
466 467

                    match v {
468
                        &Instruction_::Return(ref vec) => {
qinsoon's avatar
qinsoon committed
469 470 471
                            // change RET to a branch
                            let branch = Instruction {
                                hdr: hdr,
472 473
                                value: value.clone(),
                                ops: ops.clone(),
qinsoon's avatar
qinsoon committed
474 475
                                v: Instruction_::Branch1(Destination {
                                    target: ret_block,
476 477
                                    args: vec.iter().map(|x| DestArg::Normal(*x)).collect()
                                })
qinsoon's avatar
qinsoon committed
478 479
                            };

qinsoon's avatar
[wip]  
qinsoon committed
480
                            trace!("rewrite to: {}", branch);
qinsoon's avatar
qinsoon committed
481
                            block_content.body.push(TreeNode::new_boxed_inst(branch));
482
                        }
483 484

                        // fix destination
485
                        &Instruction_::Branch1(ref dest) => {
486 487
                            let branch = Instruction {
                                hdr: hdr,
488 489 490
                                value: value.clone(),
                                ops: ops.clone(),
                                v: Instruction_::Branch1(fix_dest(dest.clone()))
491 492
                            };

qinsoon's avatar
[wip]  
qinsoon committed
493
                            trace!("rewrite to: {}", branch);
494 495
                            block_content.body.push(TreeNode::new_boxed_inst(branch));
                        }
496 497 498 499 500
                        &Instruction_::Branch2 {
                            ref cond,
                            ref true_dest,
                            ref false_dest,
                            ref true_prob
501
                        } => {
502 503
                            let branch2 = Instruction {
                                hdr: hdr,
504 505
                                value: value.clone(),
                                ops: ops.clone(),
506
                                v: Instruction_::Branch2 {
507 508 509 510
                                    cond: *cond,
                                    true_dest: fix_dest(true_dest.clone()),
                                    false_dest: fix_dest(false_dest.clone()),
                                    true_prob: *true_prob
511
                                }
512 513
                            };

qinsoon's avatar
[wip]  
qinsoon committed
514
                            trace!("rewrite to: {}", branch2);
515 516
                            block_content.body.push(TreeNode::new_boxed_inst(branch2));
                        }
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
517 518 519 520
                        &Instruction_::Call {
                            ref data,
                            ref resume
                        } => {
521
                            let call = Instruction {
522
                                hdr: hdr,
523 524
                                value: value.clone(),
                                ops: ops.clone(),
525
                                v: Instruction_::Call {
526 527
                                    data: data.clone(),
                                    resume: fix_resume(resume.clone())
528
                                }
529 530
                            };

qinsoon's avatar
[wip]  
qinsoon committed
531
                            trace!("rewrite to: {}", call);
532 533
                            block_content.body.push(TreeNode::new_boxed_inst(call));
                        }
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
534 535 536 537
                        &Instruction_::CCall {
                            ref data,
                            ref resume
                        } => {
538
                            let call = Instruction {
539
                                hdr: hdr,
540 541
                                value: value.clone(),
                                ops: ops.clone(),
542
                                v: Instruction_::CCall {
543 544
                                    data: data.clone(),
                                    resume: fix_resume(resume.clone())
545
                                }
546 547
                            };

qinsoon's avatar
[wip]  
qinsoon committed
548
                            trace!("rewrite to: {}", call);
549 550
                            block_content.body.push(TreeNode::new_boxed_inst(call));
                        }
551 552 553 554
                        &Instruction_::Switch {
                            ref cond,
                            ref default,
                            ref branches
555
                        } => {
556 557
                            let switch = Instruction {
                                hdr: hdr,
558 559
                                value: value.clone(),
                                ops: ops.clone(),
560
                                v: Instruction_::Switch {
561 562
                                    cond: *cond,
                                    default: fix_dest(default.clone()),
Isaac Oscar Gariano's avatar
Isaac Oscar Gariano committed
563 564 565 566 567
                                    branches: branches
                                        .iter()
                                        .map(|&(ref op, ref dest)| {
                                            (op.clone(), fix_dest(dest.clone()))
                                        })
568 569
                                        .collect()
                                }
570 571
                            };

qinsoon's avatar
[wip]  
qinsoon committed
572
                            trace!("rewrite to: {}", switch);
573 574 575
                            block_content.body.push(TreeNode::new_boxed_inst(switch));
                        }

576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
                        &Instruction_::SwapStackExc {
                            stack,
                            is_exception,
                            ref args,
                            ref resume
                        } => {
                            let swapstack = Instruction {
                                hdr: hdr,
                                value: value.clone(),
                                ops: ops.clone(),
                                v: Instruction_::SwapStackExc {
                                    stack: stack,
                                    is_exception: is_exception,
                                    args: args.clone(),
                                    resume: fix_resume(resume.clone())
                                }
                            };

                            trace!("rewrite to: {}", swapstack);
                            block_content.body.push(TreeNode::new_boxed_inst(swapstack));
                        }
597 598 599
                        &Instruction_::Watchpoint { .. } |
                        &Instruction_::WPBranch { .. } |
                        &Instruction_::ExnInstruction { .. } => unimplemented!(),
600

601 602 603
                        _ => {
                            block_content.body.push(last_inst_clone);
                        }
qinsoon's avatar
qinsoon committed
604
                    }
605
                }
qinsoon's avatar
qinsoon committed
606 607 608 609 610 611 612 613 614 615 616
                _ => {
                    // do nothing, and directly push the instruction back
                    block_content.body.push(last_inst_clone)
                }
            }
        }

        caller.push(block);
    }
}

qinsoon's avatar
qinsoon committed
617
/// copies inlined function context into caller
qinsoon's avatar
qinsoon committed
618
fn copy_inline_context(caller: &mut FunctionContext, callee: &FunctionContext) {
619
    trace!("trying to copy inlined function context to caller");
qinsoon's avatar
qinsoon committed
620
    for (id, entry) in callee.values.iter() {
621 622 623
        caller
            .values
            .insert(*id, SSAVarEntry::new(entry.value().clone()));
624 625 626
    }
}

qinsoon's avatar
qinsoon committed
627
/// calculate estimate machine instruction for a Mu function
628 629 630 631 632 633 634 635 636 637 638 639 640
fn estimate_insts(fv: &MuFunctionVersion) -> usize {
    let f_content = fv.content.as_ref().unwrap();

    let mut insts = 0;

    for block in f_content.blocks.values() {
        let ref body = block.content.as_ref().unwrap().body;

        for inst in body.iter() {
            use compiler::backend;

            match inst.v {
                TreeNode_::Value(_) => unreachable!(),
641 642 643
                TreeNode_::Instruction(ref inst) => {
                    insts += backend::estimate_insts_for_ir(inst);
                }
644 645 646 647 648
            }
        }
    }

    insts
649
}